前面分別詳細介紹了python的多進程和多線程,如果還沒看前面文章的,請先看下之前的文章詳解內容。有任何疑問請留言。那這里就不再對多線程和多進程的實現和用法再贅述了。那各位同學學習了python的多線程和多進程,那么到底是多進程有優勢呢?還是多線程比較快呢?他們兩個哪個運行比較快呢?在代碼編程時到底是用多進程呢,還是用多線程呢,如何最優選擇呢?本篇文章就讓我們討論下python多進程和多線程的應用場景選擇和最優選問題。
當你理解了多線程和多進程后,你是否有過這樣的疑問,它們兩個到底誰更快呢?網上很多都會說那肯定是多進程更快了,因為線程中GIL(全局解釋器鎖)存在,實際上是串行執行的,同一時刻只能有一個線程再跑,而進程是相互獨立,多個進程是同時執行的。那么,到底是不是這樣的呢?我們通過測試一探究竟。
帶著上面的疑問進行了測試,根據測試結果圖,我們不難看出線程和進程到底哪個快,哪個效率高了。
python真正的多線程?那么根據上圖的測試結果,可以看出進程耗時比較少,線程耗時較多,那說明線程沒有進程快,似乎證實了網上很多人的想法呢。但真是這樣嗎?
我們都知道:python中的多線程需要先拿到GIL,才能執行代碼,然后釋放GIL。所以由于GIL的存在,實際上它的并發,即多個事件在同一時間是間隔的。而進程有獨立GIL,可以并行實現。因此,針對多核CPU,理論上采用多進程更能有效利用資源,效率更高,耗時更少。但是現實問題是,很多教程中經常能見到python多線程的身影。尤其是:網絡爬蟲、端口掃描等教程中。這是為什么呢,難道這些教程都是騙人的,寫教程的人都是傻子嗎?顯然不是,認為別人傻的人才是真的傻呢。那么又是問什么呢?
這里舉端口掃描的例子來說吧,代碼如下:
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()
測試結果:你會發現python多進程更快些。那么就是和我們想當然分析得出的結論相悖了嗎?
python中or的用法。那這時可能有的同學就懵逼了,那到底是多進程快的還是多線程快呢?這里,我們根據上面兩個測試不妨大膽假設一下:
CPU密集型場景下,多進程更快,效率更高;而在IO密集型場景下,多線程更有優勢。
為了驗證大家假設,代碼如下:
?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)
運行代碼從測試結果如下圖
java線程與并發編程實踐,根據上面的測試圖可以看出,cpu使用率越來越高的時(即代碼循環越多的時),兩者之間的差距越來越大。從而驗證我們的假設
CPU密集型場景下,多進程更快,效率更高;而在IO密集型場景下,多線程更有優勢。
根據上面的測試,我們得到如下結論:
在CPU密集型場景中(如:各種循環處理、計數等等),適合用多進程;
自學編程的app?在IO密集型場景中(如:文件處理、網絡爬蟲等),適合用多線程。
那么有同學看到上面的結論就明白了,多進程和多線程的效率快慢,不是始終不變的,是要應對不通場景的,不一樣的場景,用不同的方法,這才是正確的選擇。那么cpu密集型場景和io密集型場景我怎么區分和判斷呢,只有知道當前是何場景,才能做出最優的選擇。下面我就教大家來分辨場景。
簡單的辦法
直接看CPU的占用率或磁盤的IO讀寫速度。
python多線程與多進程,歸納為:計算較多就是為CPU密集型;時間等待較多(如網絡爬蟲)就是IO密集型。
下面是測試代碼,各位同學不妨自己手動測測,來讓自己有更深層的認識。
#coding=utf-8import?sysimport?multiprocessingimport?timeimport?threading??# 定義全局變量Queueg_queue?=?multiprocessing.Queue()?def?init_queue():????print("init g_queue start")????while?not?g_queue.empty():????????g_queue.get() ? ? ? ?????for?_index?in?range(10):????????g_queue.put(_index)????print("init g_queue end")????return?# 定義一個IO密集型任務:利用time.sleep()?def?task_io(task_id):????print("IOTask[%s] start"?%?task_id)????while?not?g_queue.empty():????????time.sleep(1)????????try:????????????data?=?g_queue.get(block=True, timeout=1)????????????print("IOTask[%s] get data: %s"?%?(task_id, data))????????except?Exception as excep:????????????print("IOTask[%s] error: %s"?%?(task_id,?str(excep)))????print("IOTask[%s] end"?%?task_id)????return?g_search_list?=?list(range(10000))# 定義一個計算密集型任務:利用一些復雜加減乘除、列表查找等def?task_cpu(task_id):????print("CPUTask[%s] start"?%?task_id)????while?not?g_queue.empty():????????count?=?0????????for?i?in?range(10000):????????????count?+=?pow(3*2,?3*2)?if?i?in?g_search_list?else?0????????try:????????????data?=?g_queue.get(block=True, timeout=1)????????????print("CPUTask[%s] get data: %s"?%?(task_id, data))????????except?Exception as excep:????????????print("CPUTask[%s] error: %s"?%?(task_id,?str(excep)))????print("CPUTask[%s] end"?%?task_id)????return?task_id?if?__name__?==?'__main__':????print("cpu count:", multiprocessing.cpu_count(),?"")????print(u"========== 直接執行IO密集型任務 ==========")????init_queue()????time_0?=?time.time()????task_io(0)????print(u"結束:", time.time()?-?time_0,?"")?????print("========== 多線程執行IO密集型任務 ==========")????init_queue()????time_0?=?time.time()????thread_list?=?[threading.Thread(target=task_io, args=(i,))?for?i?in?range(10)]?????for?t?in?thread_list:????????t.start()?????for?t?in?thread_list:????????if?t.is_alive():????????????t.join()????print("結束:", time.time()?-?time_0,?"")?????print("========== 多進程執行IO密集型任務 ==========")????init_queue()????time_0?=?time.time()????process_list?=?[multiprocessing.Process(target=task_io, args=(i,))?for?i?in?range(multiprocessing.cpu_count())]?????for?p?in?process_list:????????p.start()?????for?p?in?process_list:????????if?p.is_alive():????????????p.join()????print("結束:", time.time()?-?time_0,?"")?????print("========== 直接執行CPU密集型任務 ==========")????init_queue()????time_0?=?time.time()????task_cpu(0)????print("結束:", time.time()?-?time_0,?"")?????print("========== 多線程執行CPU密集型任務 ==========")????init_queue()????time_0?=?time.time()????thread_list?=?[threading.Thread(target=task_cpu, args=(i,))?for?i?in?range(10)]?????for?t?in?thread_list:????????t.start()?????for?t?in?thread_list:????????if?t.is_alive():????????????t.join()?????print("結束:", time.time()?-?time_0,?"")?????print("========== 多進程執行cpu密集型任務 ==========")????init_queue()????time_0?=?time.time()????process_list?=?[multiprocessing.Process(target=task_cpu, args=(i,))?for?i?in?range(multiprocessing.cpu_count())]?????for?p?in?process_list:????????p.start()?????for?p?in?process_list:????????if?p.is_alive():????????????p.join()?????print("結束:", time.time()?-?time_0,?"")
謝謝大家的閱讀,如果你有任何疑問和想法,請關注留言評論,第一時間為你解答心中疑惑。
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态