由於Python的GIL限制,多線程未必是CPU密集型程序的好的選擇。
多進程可以完全獨立的進程環境中運行程序,可以充分地利用多處理器。
但是進程本身的隔離性帶來的數據不共享也是一個問題。而且線程比進程輕量級。
multiprocessing
Process類
Process類遵循了Thread類的API,減少了學習難度。(幾乎和Thread類使用方法一模一樣)
上一篇文章里最后使用了多線程來解決CPU密集型的例子,但發現多線程和多線程最終執行效率幾乎相同,多線程並沒有想象中的優勢。
上一篇中多線程的例子:
#模擬CPU密集型 多線程 import threading,logging,time,random,datetime DATEFMT="%H:%M:%S" FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s" logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT) def calc(): sum = 0 for _ in range(100000000): sum += 1 start =datetime.datetime.now() t1 = threading.Thread(target=calc) t2 = threading.Thread(target=calc) t3 = threading.Thread(target=calc) t4 = threading.Thread(target=calc) t5 = threading.Thread(target=calc) t1.start() t2.start() t3.start() t4.start() t5.start() t1.join() t2.join() t3.join() t4.join() t5.join() print('aaa') delta = (datetime.datetime.now() -start).total_seconds() print(delta) #運行結果: aaa 53.135543
此例子是單線程情況下執行耗時58秒左右。
再使用多進程的例子來看一下是否可以有所不同:
#=========多進程、真正的並行、適用於CPU計算密集型=============== import multiprocessing import datetime def calc(i): sum = 0 for _ in range(100000000): sum += 1 # print(i,sum) if __name__ == "__main__": start = datetime.datetime.now() lst = [] for i in range(5): p = multiprocessing.Process(target=calc,args=(i,),name='p-{}'.format(i)) p.start() lst.append(p) for p in lst: p.join() delta = (datetime.datetime.now() - start).total_seconds() print(delta) 運行結果: 24.767709
從耗時結果可以看出多線程的執行效率明顯得要比多線程(其實就是單線程)高得多。
進程間同步:
進程間同步提供了和線程同步一樣的類,使用方法一樣,使用的效果也類似。
不過,進程間代價要高於線程,而且底層😡實現是不同的,只不過Python屏蔽了這些,讓用戶簡單使用。
multiprocessing還提供共享內存、服務器進程來共享數據,還提供了Queue隊列、Pipe管道用於進程間通信。
通信方式不同:
多線程就是啟動多個解釋器進程,進程間通信必須序列化、反序列化
數據的線程安全性問題
由於每個進程中沒有實現多線程,GIL可以說沒什么用了。
進程池舉例:
進程池只能由創建它的進程使用
# 進程池 import multiprocessing import datetime def calc(i): sum = 0 for _ in range(100000000): sum += 1 if __name__ == "__main__": start = datetime.datetime.now() pool = multiprocessing.Pool(5) #定義同時至多起幾個線程 for i in range(5): pool.apply_async(calc,args=(i,)) pool.close() pool.join() #多線程 # lst = [] # for i in range(5): # p = multiprocessing.Process(target=calc,args=(i,),name='p-{}'.format(i)) # p.start() # lst.append(p) # # for p in lst: # p.join() delta = (datetime.datetime.now() - start).total_seconds() print(delta) 運行結果: 24.944169
多進程、多線程的選擇
1、CPU密集型
CPython中使用到了GIL,多線程的時候鎖相互競爭,且多核優勢不能發揮,Python多進程效率更高。
2、IO密集型
適合是用多線程,減少IO序列化開銷。且在IO等待的時候,切換到其它線程繼續執行,效率不錯。
應用:
請求/應答模型:WEB應用中常見的處理模型
master啟動多個worker工作進程,一般和CPU數目相同。
worker工作進程中啟動多線程,提高並發處理能力。worker處理用戶的請求,往往需要等待數據。
這就是nginx工作模式。