目的
前面分別詳細介紹了python的多進程和多線程,如果還沒看前面文章的,請先看下之前的文章詳解內容。有任何疑問請留言。那這裡就不再對多線程和多進程的實現和用法再贅述了。那各位同學學習了python的多線程和多進程,那麼到底是多進程有優勢呢?還是多線程比較快呢?他們兩個哪個運行比較快呢?在代碼編程時到底是用多進程呢,還是用多線程呢,如何最優選擇呢?本篇文章就讓我們討論下python多進程和多線程的應用場景選擇和最優選問題。
![Python多進程和多線程測試比高低,只為證明誰是最快的“仔”](http://p2.ttnews.xyz/loading.gif)
python最優選
進程快的佐證測試
當你理解了多線程和多進程後,你是否有過這樣的疑問,它們兩個到底誰更快呢?網上很多都會說那肯定是多進程更快了,因為線程中GIL(全局解釋器鎖)存在,實際上是串行執行的,同一時刻只能有一個線程再跑,而進程是相互獨立,多個進程是同時執行的。那麼,到底是不是這樣的呢?我們通過測試一探究竟。
帶著上面的疑問進行了測試,根據測試結果圖,我們不難看出線程和進程到底哪個快,哪個效率高了。
![Python多進程和多線程測試比高低,只為證明誰是最快的“仔”](http://p2.ttnews.xyz/loading.gif)
線程和進程對比圖
那麼根據上圖的測試結果,可以看出進程耗時比較少,線程耗時較多,那說明線程沒有進程快,似乎證實了網上很多人的想法呢。但真是這樣嗎?
線程快的佐證測試
我們都知道: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>
謝謝大家的閱讀,如果你有任何疑問和想法,請關注留言評論,第一時間為你解答心中疑惑。
閱讀更多 豌豆多多追夢記 的文章