由於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工作模式。
