Python多进程和多线程测试比高低,只为证明谁是最快的“仔”

目的

前面分别详细介绍了python的多进程和多线程,如果还没看前面文章的,请先看下之前的文章详解内容。有任何疑问请留言。那这里就不再对多线程和多进程的实现和用法再赘述了。那各位同学学习了python的多线程和多进程,那么到底是多进程有优势呢?还是多线程比较快呢?他们两个哪个运行比较快呢?在代码编程时到底是用多进程呢,还是用多线程呢,如何最优选择呢?本篇文章就让我们讨论下python多进程和多线程的应用场景选择和最优选问题。


Python多进程和多线程测试比高低,只为证明谁是最快的“仔”

python最优选

进程快的佐证测试

当你理解了多线程和多进程后,你是否有过这样的疑问,它们两个到底谁更快呢?网上很多都会说那肯定是多进程更快了,因为线程中GIL(全局解释器锁)存在,实际上是串行执行的,同一时刻只能有一个线程再跑,而进程是相互独立,多个进程是同时执行的。那么,到底是不是这样的呢?我们通过测试一探究竟。

带着上面的疑问进行了测试,根据测试结果图,我们不难看出线程和进程到底哪个快,哪个效率高了。


Python多进程和多线程测试比高低,只为证明谁是最快的“仔”

线程和进程对比图

那么根据上图的测试结果,可以看出进程耗时比较少,线程耗时较多,那说明线程没有进程快,似乎证实了网上很多人的想法呢。但真是这样吗?

线程快的佐证测试

我们都知道: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>

运行代码从测试结果如下图


Python多进程和多线程测试比高低,只为证明谁是最快的“仔”

根据上面的测试图可以看出,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)return​g_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_id​if__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>

谢谢大家的阅读,如果你有任何疑问和想法,请关注留言评论,第一时间为你解答心中疑惑。


分享到:


相關文章: