很多人说Python多线程是鸡肋?

在给大家分享之前,这里推荐下我自己建的python群:595266089,不管你是小白还是大牛,小编我都挺欢迎,不定期分享干货,包括2017最新的python企业案例学习资料和零基础入门教程,欢迎初学和进阶中的小伙伴。

当初在刚学习python多线程时,上网搜索资料几乎都是一片倒的反应python没有真正意义上的多线程,python多线程就是鸡肋。当时不明所以,只是了解到python带有GIL解释器锁的概念,同一时刻只能有一个线程在运行,遇到IO操作才会释放切换。那么,python多线程是否真的很鸡肋呢?要解决这个疑惑,我想必须亲自动手测试。

经过对比python与java的多线程测试,我发现python多线程的效率确实不如java,但远还没有达到鸡肋的程度,那么跟其他机制相比较呢?

观点:用多进程替代多线程需求

辗转了多篇博文,我看到了一些网友的观点,觉得应该使用python多进程来代替多线程的需求,因为多进程不受GIL的限制。于是我便动手使用多进程去解决一些并发问题,期间也遇到了一些坑,所幸大部分查找资料解决了,然后对多进程做了简单汇总介绍Python多进程。

进群:960410445  即可获取数十套PDF!

那么是否多进程能完全替代多线程呢?别急,我们继续往下看。

观点:协程为最佳方案

协程的概念目前来说是比较火热的,协程不同于线程的地方在于协程不是操作系统进行切换,而是由程序员编码进行切换的,也就是说切换是由程序员控制的,这样就没有了线程所谓的安全问题。协程的概念非常广而深,本文暂不做具体介绍,以后会单独成文。

测试数据

好了,网上的观点无非是使用多进程或者协程来代替多线程(当然换编程语言,换解释器之类方法除外),那么我们就来测试下这三者的性能之差。既然要公平测试,就应该考虑IO密集型与cpu密集型的问题,所以分两组数据进行测试。

IO密集型测试

测试IO密集型,我选择最常用的爬虫功能,计算爬虫访问bing所需要的时间。(主要测试多线程与协程,单线程与多进程就不测了,因为没有必要)

测试代码

#! -*- coding:utf-8 -*-

fromgevent importmonkey;monkey.patch_all()

importgevent

importtime

importthreading

importurllib2

defurllib2_(url):

try:

urllib2.urlopen(url,timeout=10).read()

exceptException,e:

printe

defgevent_(urls):

jobs=[gevent.spawn(urllib2_,url)forurl inurls]

gevent.joinall(jobs,timeout=10)

foriinjobs:

i.join()

defthread_(urls):

a=[]

forurl inurls:

t=threading.Thread(target=urllib2_,args=(url,))

a.append(t)

foriina:

i.start()

foriina:

i.join()

if__name__=="__main__":

urls=["https://www.bing.com/"]*10

t1=time.time()

gevent_(urls)

t2=time.time()

print'gevent-time:%s' % str(t2-t1)

thread_(urls)

t4=time.time()

print'thread-time:%s' % str(t4-t2)

测试结果:

访问10次

gevent-time:0.380326032639

thread-time:0.376606941223

访问50次

gevent-time:1.3358900547

thread-time:1.59564089775

访问100次

gevent-time:2.42984986305

thread-time:2.5669670105

访问300次

gevent-time:6.66330099106

thread-time:10.7605059147

从结果可以看出,当并发数不断增大时,协程的效率确实比多线程要高,但在并发数不是那么高时,两者差异不大。

cpu密集型

cpu密集型,我选择科学计算的一些功能,计算所需时间。(主要测试单线程、多线程、协程、多进程)

测试代码

#! -*- coding:utf-8 -*-

frommultiprocessingimportProcess aspro

frommultiprocessing.dummyimportProcess asthr

fromgevent importmonkey;monkey.patch_all()

importgevent

defrun(i):

lists=range(i)

list(set(lists))

if__name__=="__main__":

'''

多进程

'''

foriinrange(30): ##10-2.1s 20-3.8s 30-5.9s

t=pro(target=run,args=(5000000,))

t.start()

'''

多线程

'''

# for i in range(30): ##10-3.8s 20-7.6s 30-11.4s

# t=thr(target=run,))

# t.start()

'''

协程

'''

# jobs=[gevent.spawn(run,5000000) for i in range(30)] ##10-4.0s 20-7.7s 30-11.5s

# gevent.joinall(jobs)

# for i in jobs:

# i.join()

'''

单线程

'''

# for i in range(30): ##10-3.5s 20-7.6s 30-11.3s

# run(5000000)

测试结果:

  • 并发10次:【多进程】2.1s 【多线程】3.8s 【协程】4.0s 【单线程】3.5s

  • 并发20次:【多进程】3.8s 【多线程】7.6s 【协程】7.7s 【单线程】7.6s

  • 并发30次:【多进程】5.9s 【多线程】11.4s 【协程】11.5s 【单线程】11.3s

可以看到,在cpu密集型的测试下,多进程效果明显比其他的好,多线程、协程与单线程效果差不多。这是因为只有多进程完全使用了cpu的计算能力。在代码运行时,我们也能够看到,只有多进程可以将cpu使用率占满。

本文结论

从两组数据我们不难发现,python多线程并没有那么鸡肋。如若不然,python3为何不去除GIL呢?对于此问题,Python社区也有两派意见,这里不再论述,我们应该尊重Python之父的决定。

至于何时该用多线程,何时用多进程,何时用协程?想必答案已经很明显了。

当我们需要编写并发爬虫等IO密集型的程序时,应该选用多线程或者协程(亲测差距不是特别明显);当我们需要科学计算,设计cpu密集型程序,应该选用多进程。当然以上结论的前提是,不做分布式,只在一台服务器上测试。

答案已经给出,本文是否就此收尾?既然已经论述Python多线程尚有用武之地,那么就来介绍介绍其用法吧。

Multiprocessing.dummy模块

Multiprocessing.dummy用法与多进程Multiprocessing用法类似,只是在import包的时候,加上.dummy。

用法参考Multiprocessing用法

threading模块

这是python自带的threading多线程模块,其创建多线程主要有2种方式。一种为继承threading类,另一种使用threading.Thread函数,接下来将会分别介绍这两种用法

Usage【1】

利用threading.Thread()函数创建线程。

代码

defrun(i):

printi

foriinrange(10):

t=threading.Thread(target=run,args=(i,))

t.start()

说明:Thread()函数有2个参数,一个是target,内容为子线程要执行的函数名称;另一个是args,内容为需要传递的参数。创建完子线程,将会返回一个对象,调用对象的start方法,可以启动子线程。

线程对象的方法

  • Start() 开始线程的执行

  • Run() 定义线程的功能函数

  • Join(timeout=None) 程序挂起,直到线程结束;如果给了timeout,则最多阻塞timeout秒

  • getName() 返回线程的名字

  • setName() 设置线程的名字

  • isAlive() 布尔标志,表示这个线程是否还在运行

  • isDaemon() 返回线程的daemon标志

  • setDaemon(daemonic) 把线程的daemon标志设为daemonic(一定要在start()函数调用

  • t.setDaemon(True) 把父线程设置为守护线程,当父进程结束时,子进程也结束。

threading类的方法

  • threading.enumerate() 正在运行的线程数量

Usage【2】

通过继承threading类,创建线程。

代码

importthreading

classtest(threading.Thread):

def__init__(self):

threading.Thread.__init__(self)

defrun(self):

try:

print"code one"

except:

pass

foriinrange(10):

cur=test()

cur.start()

foriinrange(10):

cur.join()

说明:此方法继承了threading类,并且重构了run函数功能

获取线程返回值问题

有时候,我们往往需要获取每个子线程的返回值。然而通过调用普通函数获取return值的方式在多线程中并不适用。因此需要一种新的方式去获取子线程返回值。

代码

importthreading

classtest(threading.Thread):

def__init__(self):

threading.Thread.__init__(self)

defrun(self):

self.tag=1

defget_result(self):

ifself.tag==1:

returnTrue

else:

returnFalse

f=test()

f.start()

whilef.isAlive():

continue

printf.get_result()

说明:多线程获取返回值的首要问题,就是子线程什么时候结束?我们应该什么时候去获取返回值?可以使用isAlive()方法判断子线程是否存活。

控制线程运行数目

当需要执行的任务非常多时,我们往往需要控制线程的数量,threading类自带有控制线程数量方法

代码

importthreading

maxs=10##并发的线程数量

threadLimiter=threading.BoundedSemaphore(maxs)

classtest(threading.Thread):

def__init__(self):

threading.Thread.__init__(self)

defrun(self):

threadLimiter.acquire()#获取

try:

print"code one"

except:

pass

finally:

threadLimiter.release()#释放

foriinrange(100):

cur=test()

cur.start()

foriinrange(100):

cur.join()

说明:以上程序可以控制多线程并发数为10,超过这个数量会引发异常。

除了自带方法,我们还可以设计其他方案:

threads=[]

'''

创建所有线程

'''

foriinrange(10):

t=threading.Thread(target=run,))

threads.append(t)

'''

启动列表中的线程

'''

fortinthreads:

t.start()

whileTrue:

#判断正在运行的线程数量,如果小于5则退出while循环,

#进入for循环启动新的进程.否则就一直在while循环进入死循环

if(len(threading.enumerate())<5):

break

以上两种方式皆可以,本人更喜欢用下面那种方式。

线程池

importthreadpool

defThreadFun(arg1,arg2):

pass

defmain():

device_list=[object1,object2,object3......,objectn]#需要处理的设备个数

task_pool=threadpool.ThreadPool(8)#8是线程池中线程的个数

request_list=[]#存放任务列表

#首先构造任务列表

fordeviceindevice_list:

request_list.append(threadpool.makeRequests(ThreadFun,[((device,),{})]))

#将每个任务放到线程池中,等待线程池中线程各自读取任务,然后进行处理,使用了map函数,不了解的可以去了解一下。

map(task_pool.putRequest,request_list)

#等待所有任务处理完成,则返回,如果没有处理完,则一直阻塞

task_pool.poll()

if__name__=="__main__":

main()

多进程问题,可以赶赴Python多进程(http://python.jobbole.com/87760/)现场,其他关于多线程问题,可以下方留言讨论

申明:本文谈不上原创,其中借鉴了网上很多大牛的文章,本人只是在此测试论述Python多线程相关问题,并简单介绍Python多线程的基本用法,为新手朋友解惑。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我最近重新拾起了计算机视觉,借助Python的opencv还有face_recognition库写了个简单的图像识别demo,额外定制了一些内容,原本想打包成exe然后发给朋友,不过在这当中遇到了许多小问题,都解决了,记录一下踩过的坑。 1、Pyinstaller打包过程当中出现warning,跟d
说到Pooling,相信学习过CNN的朋友们都不会感到陌生。Pooling在中文当中的意思是“池化”,在神经网络当中非常常见,通常用的比较多的一种是Max Pooling,具体操作如下图: 结合图像理解,相信你也会大概明白其中的本意。不过Pooling并不是只可以选取2x2的窗口大小,即便是3x3,
记得大一学Python的时候,有一个题目是判断一个数是否是复数。当时觉得比较复杂不好写,就琢磨了一个偷懒的好办法,用异常处理的手段便可以大大程度帮助你简短代码(偷懒)。以下是判断整数和复数的两段小代码: 相信看到这里,你也有所顿悟,能拓展出更多有意思的方法~
文章目录 3 直方图Histogramplot1. 基本直方图的绘制 Basic histogram2. 数据分布与密度信息显示 Control rug and density on seaborn histogram3. 带箱形图的直方图 Histogram with a boxplot on t
文章目录 5 小提琴图Violinplot1. 基础小提琴图绘制 Basic violinplot2. 小提琴图样式自定义 Custom seaborn violinplot3. 小提琴图颜色自定义 Control color of seaborn violinplot4. 分组小提琴图 Group
文章目录 4 核密度图Densityplot1. 基础核密度图绘制 Basic density plot2. 核密度图的区间控制 Control bandwidth of density plot3. 多个变量的核密度图绘制 Density plot of several variables4. 边
首先 import tensorflow as tf tf.argmax(tenso,n)函数会返回tensor中参数指定的维度中的最大值的索引或者向量。当tensor为矩阵返回向量,tensor为向量返回索引号。其中n表示具体参数的维度。 以实际例子为说明: import tensorflow a
seaborn学习笔记章节 seaborn是一个基于matplotlib的Python数据可视化库。seaborn是matplotlib的高级封装,可以绘制有吸引力且信息丰富的统计图形。相对于matplotlib,seaborn语法更简洁,两者关系类似于numpy和pandas之间的关系,seabo
Python ConfigParser教程显示了如何使用ConfigParser在Python中使用配置文件。 文章目录 1 介绍1.1 Python ConfigParser读取文件1.2 Python ConfigParser中的节1.3 Python ConfigParser从字符串中读取数据
1. 处理Excel 电子表格笔记(第12章)(代码下载) 本文主要介绍openpyxl 的2.5.12版处理excel电子表格,原书是2.1.4 版,OpenPyXL 团队会经常发布新版本。不过不用担心,新版本应该在相当长的时间内向后兼容。如果你有新版本,想看看它提供了什么新功能,可以查看Open
1. 发送电子邮件和短信笔记(第16章)(代码下载) 1.1 发送电子邮件 简单邮件传输协议(SMTP)是用于发送电子邮件的协议。SMTP 规定电子邮件应该如何格式化、加密、在邮件服务器之间传递,以及在你点击发送后,计算机要处理的所有其他细节。。但是,你并不需要知道这些技术细节,因为Python 的
文章目录 12 绘图实例(4) Drawing example(4)1. Scatterplot with varying point sizes and hues(relplot)2. Scatterplot with categorical variables(swarmplot)3. Scat
文章目录 10 绘图实例(2) Drawing example(2)1. Grouped violinplots with split violins(violinplot)2. Annotated heatmaps(heatmap)3. Hexbin plot with marginal dist
文章目录 9 绘图实例(1) Drawing example(1)1. Anscombe’s quartet(lmplot)2. Color palette choices(barplot)3. Different cubehelix palettes(kdeplot)4. Distribution
Python装饰器教程展示了如何在Python中使用装饰器基本功能。 文章目录 1 使用教程1.1 Python装饰器简单示例1.2 带@符号的Python装饰器1.3 用参数修饰函数1.4 Python装饰器修改数据1.5 Python多层装饰器1.6 Python装饰器计时示例 2 参考 1 使
1. 用GUI 自动化控制键盘和鼠标第18章 (代码下载) pyautogui模块可以向Windows、OS X 和Linux 发送虚拟按键和鼠标点击。根据使用的操作系统,在安装pyautogui之前,可能需要安装一些其他模块。 Windows: 不需要安装其他模块。OS X: sudo pip3
文章目录 生成文件目录结构多图合并找出文件夹中相似图像 生成文件目录结构 生成文件夹或文件的目录结构,并保存结果。可选是否滤除目录,特定文件以及可以设定最大查找文件结构深度。效果如下: root:[z:/] |--a.py |--image | |--cat1.jpg | |--cat2.jpg |
文章目录 VENN DIAGRAM(维恩图)1. 具有2个分组的基本的维恩图 Venn diagram with 2 groups2. 具有3个组的基本维恩图 Venn diagram with 3 groups3. 自定义维恩图 Custom Venn diagram4. 精致的维恩图 Elabo
mxnet60分钟入门Gluon教程代码下载,适合做过深度学习的人使用。入门教程地址: https://beta.mxnet.io/guide/getting-started/crash-course/index.html mxnet安装方法:pip install mxnet 1 在mxnet中使
文章目录 1 安装2 快速入门2.1 基本用法2.2 输出图像格式2.3 图像style设置2.4 属性2.5 子图和聚类 3 实例4 如何进一步使用python graphviz Graphviz是一款能够自动排版的流程图绘图软件。python graphviz则是graphviz的python实