二十四、深入Python多進程multiprocessing模組
@Author: Runsen
threading
包為 Python 提供了執行緒模型,而 multiprocessing
包則為另一種並發模型 — 多進程模型提供了強大的解決方案。
multiprocessing
multiprocessing
包是Python中的多進程管理包。與之前的threading.Thread
類似,它可以利用multiprocessing.Process
對象來創建一個進程。
multiprocessing
的模組的API非常簡單直觀,可以讓新手迅速上手在多個進程之間劃分工作。我們現在看一個官方簡單的例子:
# importing the multiprocessing module
import multiprocessing
def print_cube(num):
print("Cube: {}".format(num * num * num))
def print_square(num):
print("Square: {}".format(num * num))
if __name__ == "__main__":
# creating processes
p1 = multiprocessing.Process(target=print_square, args=(10, ))
p2 = multiprocessing.Process(target=print_cube, args=(10, ))
# starting process 1&2
p1.start()
p2.start()
# wait until process 1&2 is finished
p1.join()
p2.join()
# both processes finished
print("Done!")
運行結果是這樣的:
Square: 100
Cube: 1000
Done!
上面程式碼中導入了multiprocessing
模組,隨後定義了兩個函數,它們的功能分別是列印一個數的三次方和列印一個數的平方。
之後關鍵的步驟來了,要創建多個進程,首先需要創建Process
類的對象。在這個例子中Process
類接收了兩個參數:
- target:在進程中被執行的函數
- args:向被執行函數傳遞的參數
執行緒池
ThreadPool類提供一個執行緒池,該執行緒池可用於發送工作項、處理非同步 I/O、代表其他執行緒等待以及處理計時器。
通常可導入模組:from multiprocessing import Pool
。
創建執行緒池:pool = Pool(10)
官方提供了比較常見的三種使用執行緒池的方法。分別是map
,apply
和apply_async
。
Map
其中map
規定執行緒池執行的任務:result = pool.map(func,datalist)
func
為所要執行的函數,datalist
為參數列表,執行緒池也會依次在參數列表中提取參數帶入函數中來執行函數,參數列表的長度也就是執行緒池所要執行的任務數量。
# 進程池即創建一個進程庫,事先設置好需要多少進程,從進程庫中提取這些已創建的進程即可。
# Pool.map()
import multiprocessing
def test(i):
print(i)
if __name__ == '__main__':
list1 = list(range(5)) # 創建一個列表,map方法會用到
pool1 = multiprocessing.Pool(processes=4) # 創建一個進程池,這裡創建了含有4個進程的進程池
pool1.map(test, list1)
'''
對pool1使用map方法,在這個例子中即形成(test(0),test(1),test(2),test(3),test(4) 這些任務)
這些將執行的任務是按順序執行的,也就是說進程1執行test(0),進程2執行test(1),進程3執行test(2),
進程4執行test(3),還多出一個任務則需要等待,等到某個進程提前結束了就再執行這個任務。
這樣的話相當於一個進程執行一次單獨的任務,十分方便某些任務
如果函數有返回值即return,還可以使用result = pool1.map(test,list1)獲取每個進程的返回值,
但是這裡是所有返回值在一起,需要自己對其按需處理。
'''
pool1.close() # close方法用於關閉進程池,即恢復到沒有子進程的情況
pool1.join()
除了map
方法,還有apply
和apply_async
方法用來執行進程,允許多個進程同時進入池子。
apply
在multiprocessing
模組中,apply
阻塞主進程, 並且一個一個按順序地執行子進程, 等到全部子進程都執行完畢後 ,繼續執行 apply()
後面主進程的程式碼。
import time
import multiprocessing
def doIt(num):
print("Process num is : %s" % num)
time.sleep(1)
print('process %s end' % num)
if __name__ == '__main__':
print('mainProcess start')
# 記錄一下開始執行的時間
start_time = time.time()
# 創建三個子進程
pool = multiprocessing.Pool(3)
print('Child start')
for i in range(3):
pool.apply(doIt, [i])
print('mainProcess done time:%s s' % (time.time() - start_time))
pool.close()
pool.join()
輸出如下所示
mainProcess start
Child start
Process num is : 0
process 0 end
Process num is : 1
process 1 end
Process num is : 2
process 2 end
mainProcess done time:3.7142281532287598 s
執行結果 我們可以看到, 主進程開始執行之後, 創建的三個子進程也隨即開始執行, 主進程被阻塞, 而且接下來三個子進程是一個接一個按順序地執行, 等到子進程全部執行完畢之後, 主進程就會繼續執行, 列印出最後一句。
apply_async
接下來是使用apply_async()
, 只需要把上面的程式碼使用 apply()
的地方改成apply_async()
即可, 程式碼如下。
import time
import multiprocessing
def doIt(num):
print("Process num is : %s" % num)
time.sleep(1)
print('process %s end' % num)
if __name__ == '__main__':
print('mainProcess start')
# 記錄一下開始執行的時間
start_time = time.time()
# 創建三個子進程
pool = multiprocessing.Pool(3)
print('Child start')
for i in range(3):
pool.apply_async(doIt, [i])
print('mainProcess done time:%s s' % (time.time() - start_time))
pool.close()
pool.join()
輸出如下所示
mainProcess start
Child start
mainProcess done time:0.060953378677368164 s
Process num is : 0
Process num is : 1
Process num is : 2
process 0 end
process 1 end
process 2 end
apply_async()
非阻塞非同步的, 他不會等待子進程執行完畢, 主進程會繼續執行, 他會根據系統調度來進行進程切換。
CPU在執行第一個子進程的時候, 還沒等第一個子進程結束, 系統調度到了按順序調度到了第二個子進程, 以此類推, 一直調度運行子進程, 一個接一個地結束子進程的運行, 最後運行主進程, 而且我們可以看到使用apply_async()
的執行效力會更高, 你看一下他們各自執行結果最後一句的執行消耗時間就知道了, 這也是官方推薦我們使用apply_async()
的主要原因吧
參考://docs.python.org/zh-cn/3.6/
今天也學到了很多東西呢,明天有什麼新知識呢?真期待鴨如果喜歡文章可以關注我哦
本文已收錄 GitHub,傳送門~ ,裡面更有大廠面試完整考點,歡迎 Star。