目的
前面分别详细介绍了python的多进程和多线程,如果还没看前面文章的,请先看下之前的文章详解内容。有任何疑问请留言。那这里就不再对多线程和多进程的实现和用法再赘述了。那各位同学学习了python的多线程和多进程,那么到底是多进程有优势呢?还是多线程比较快呢?他们两个哪个运行比较快呢?在代码编程时到底是用多进程呢,还是用多线程呢,如何最优选择呢?本篇文章就让我们讨论下python多进程和多线程的应用场景选择和最优选问题。
进程快的佐证测试
当你理解了多线程和多进程后,你是否有过这样的疑问,它们两个到底谁更快呢?网上很多都会说那肯定是多进程更快了,因为线程中GIL(全局解释器锁)存在,实际上是串行执行的,同一时刻只能有一个线程再跑,而进程是相互独立,多个进程是同时执行的。那么,到底是不是这样的呢?我们通过测试一探究竟。
带着上面的疑问进行了测试,根据测试结果图,我们不难看出线程和进程到底哪个快,哪个效率高了。
那么根据上图的测试结果,可以看出进程耗时比较少,线程耗时较多,那说明线程没有进程快,似乎证实了网上很多人的想法呢。但真是这样吗?
线程快的佐证测试
我们都知道:python中的多线程需要先拿到GIL,才能执行代码,然后释放GIL。所以由于GIL的存在,实际上它的并发,即多个事件在同一时间是间隔的。而进程有独立GIL,可以并行实现。因此,针对多核CPU,理论上采用多进程更能有效利用资源,效率更高,耗时更少。但是现实问题是,很多教程中经常能见到python多线程的身影。尤其是:网络爬虫、端口扫描等教程中。这是为什么呢,难道这些教程都是骗人的,写教程的人都是傻子吗?显然不是,认为别人傻的人才是真的傻呢。那么又是问什么呢?
这里举端口扫描的例子来说吧,代码如下:
<code>import sys,threadingfrom socket import * host = "127.0.0.1" if len(sys.argv)==1 else sys.argv[1]portList = [i for i in range(1,1000)]scanList = []lock = threading.Lock()print('Please waiting... From ',host) def scanPort(port): try: tcp = socket(AF_INET,SOCK_STREAM) tcp.connect((host,port)) except: pass else: if lock.acquire(): print('[+]port',port,'open') lock.release() finally: tcp.close() for p in portList: t = threading.Thread(target=scanPort,args=(p,)) scanList.append(t)for i in range(len(portList)): scanList[i].start()for i in range(len(portList)): scanList[i].join()/<code>
测试结果:你会发现python多进程更快些。那么就是和我们想当然分析得出的结论相悖了吗?
假设
那这时可能有的同学就懵逼了,那到底是多进程快的还是多线程快呢?这里,我们根据上面两个测试不妨大胆假设一下:
CPU密集型场景下,多进程更快,效率更高;而在IO密集型场景下,多线程更有优势。
为了验证大家假设,代码如下:
<code>import timeimport threadingimport multiprocessing max_process = 4max_thread = max_process def fun(n,n2): #cpu密集型 for i in range(0,n): for j in range(0,(int)(n*n*n*n2)): t = i*j def thread_main(n2): thread_list = [] for i in range(0,max_thread): t = threading.Thread(target=fun,args=(50,n2)) thread_list.append(t) start = time.time() print(' [+] much thread start') for i in thread_list: i.start() for i in thread_list: i.join() print(' [-] much thread use ',time.time()-start,'s') def process_main(n2): p = multiprocessing.Pool(max_process) for i in range(0,max_process): p.apply_async(func = fun,args=(50,n2)) start = time.time() print(' [+] much process start') p.close()#关闭进程池 p.join()#等待所有子进程完毕 print(' [-] much process use ',time.time()-start,'s') if __name__=='__main__': print("[++]When n=50,n2=0.1:") thread_main(0.1) process_main(0.1) print("[++]When n=50,n2=1:") thread_main(1) process_main(1) print("[++]When n=50,n2=10:") thread_main(10) process_main(10)/<code>
运行代码从测试结果如下图
根据上面的测试图可以看出,cpu使用率越来越高的时(即代码循环越多的时),两者之间的差距越来越大。从而验证我们的假设
CPU密集型场景下,多进程更快,效率更高;而在IO密集型场景下,多线程更有优势。
结论
根据上面的测试,我们得到如下结论:
在CPU密集型场景中(如:各种循环处理、计数等等),适合用多进程;
在IO密集型场景中(如:文件处理、网络爬虫等),适合用多线程。
方法选择
那么有同学看到上面的结论就明白了,多进程和多线程的效率快慢,不是始终不变的,是要应对不通场景的,不一样的场景,用不同的方法,这才是正确的选择。那么cpu密集型场景和io密集型场景我怎么区分和判断呢,只有知道当前是何场景,才能做出最优的选择。下面我就教大家来分辨场景。
简单的办法
直接看CPU的占用率或磁盘的IO读写速度。
归纳为:计算较多就是为CPU密集型;时间等待较多(如网络爬虫)就是IO密集型。
下面是测试代码,各位同学不妨自己手动测测,来让自己有更深层的认识。
<code>#coding=utf-8importsysimportmultiprocessingimporttimeimportthreading# 定义全局变量Queueg_queue=multiprocessing.Queue()definit_queue():print("init g_queue start")whilenotg_queue.empty():g_queue.get() for_indexinrange(10):g_queue.put(_index)print("init g_queue end")return# 定义一个IO密集型任务:利用time.sleep()deftask_io(task_id):print("IOTask[%s] start"%task_id)whilenotg_queue.empty():time.sleep(1)try:data=g_queue.get(block=True, timeout=1)print("IOTask[%s] get data: %s"%(task_id, data))exceptException as excep:print("IOTask[%s] error: %s"%(task_id,str(excep)))print("IOTask[%s] end"%task_id)returng_search_list=list(range(10000))# 定义一个计算密集型任务:利用一些复杂加减乘除、列表查找等deftask_cpu(task_id):print("CPUTask[%s] start"%task_id)whilenotg_queue.empty():count=0foriinrange(10000):count+=pow(3*2,3*2)ifiing_search_listelse0try:data=g_queue.get(block=True, timeout=1)print("CPUTask[%s] get data: %s"%(task_id, data))exceptException as excep:print("CPUTask[%s] error: %s"%(task_id,str(excep)))print("CPUTask[%s] end"%task_id)returntask_idif__name__=='__main__':print("cpu count:", multiprocessing.cpu_count(),"\\n")print(u"========== 直接执行IO密集型任务 ==========")init_queue()time_0=time.time()task_io(0)print(u"结束:", time.time()-time_0,"\\n")print("========== 多线程执行IO密集型任务 ==========")init_queue()time_0=time.time()thread_list=[threading.Thread(target=task_io, args=(i,))foriinrange(10)]fortinthread_list:t.start()fortinthread_list:ift.is_alive():t.join()print("结束:", time.time()-time_0,"\\n")print("========== 多进程执行IO密集型任务 ==========")init_queue()time_0=time.time()process_list=[multiprocessing.Process(target=task_io, args=(i,))foriinrange(multiprocessing.cpu_count())]forpinprocess_list:p.start()forpinprocess_list:ifp.is_alive():p.join()print("结束:", time.time()-time_0,"\\n")print("========== 直接执行CPU密集型任务 ==========")init_queue()time_0=time.time()task_cpu(0)print("结束:", time.time()-time_0,"\\n")print("========== 多线程执行CPU密集型任务 ==========")init_queue()time_0=time.time()thread_list=[threading.Thread(target=task_cpu, args=(i,))foriinrange(10)]fortinthread_list:t.start()fortinthread_list:ift.is_alive():t.join()print("结束:", time.time()-time_0,"\\n")print("========== 多进程执行cpu密集型任务 ==========")init_queue()time_0=time.time()process_list=[multiprocessing.Process(target=task_cpu, args=(i,))foriinrange(multiprocessing.cpu_count())]forpinprocess_list:p.start()forpinprocess_list:ifp.is_alive():p.join()print("结束:", time.time()-time_0,"\\n")/<code>
谢谢大家的阅读,如果你有任何疑问和想法,请关注留言评论,第一时间为你解答心中疑惑。
閱讀更多 豌豆多多追夢記 的文章