本文鏈接:https://www.cnblogs.com/tujia/p/13686684.html
背景:來觀察測試一下python 進程(process)的阻塞、普通進程和守護進程又有什么區別、進程池又是什么、進程池怎么異步提交任務等等
一、公共代碼
首先先貼上一些公共代碼,下面的例子都基於這份公共代碼運行(注:替換xxx的內容)
import time import multiprocessing def worker(name): print('%s: %s start...' % (time.strftime('%X'), name)) time.sleep(2) print('%s: %s done.' % (time.strftime('%X'), name)) def xxx(): pass if __name__ == '__main__': xxx()
二、單進程阻塞
def 單進程阻塞(): t = multiprocessing.Process(target=worker, args=('張三',)) t.start() # 阻塞 t.join() print('Finished')
運行結果:
解釋:阻塞進程的情況下,程序會先等待進程任務執行完,再往下執行其他代碼
三、單進程不阻塞
def 單進程不阻塞(): t = multiprocessing.Process(target=worker, args=('李四',)) t.start() print('Finished')
運行結果:
解釋:不阻塞進程的情況下,程序會直接往下走,進程任務是后完成的(因為我在進程任務里加了 sleep),類似於異步;同時,我們還可以發現,程序執行完最后一行代碼之后,如果進程任務還沒完成,程序是不會馬上死掉的,還是會等進程任務執行完才會結束程序。
四、多進程的錯誤阻塞
def 多進程的錯誤阻塞(): t1 = multiprocessing.Process(target=worker, args=('張三',)) t1.start() t1.join() t2 = multiprocessing.Process(target=worker, args=('李四',)) t2.start() t2.join() print('Finished')
運行結果:
解釋:t1.join 直接阻塞了程序,t2還沒start,t1.join阻塞程序直到t1的任務已完成。所以會看到 張三 done 之后,李四 才能 start
五、多進程的正確阻塞
def 多進程的正確阻塞(): t1 = multiprocessing.Process(target=worker, args=('張三',)) t2 = multiprocessing.Process(target=worker, args=('李四',)) t1.start() t2.start() t1.join() t2.join() print('Finished')
運行結果:
解釋:需要將所有進程都start之后,才能阻塞(join);進程任務也不是按順序執行和完成了,哪個先完成,得看哪個進程任務耗時少,也有可能會同時完成(並發)
按下面代碼,修改一下worker函數,給李四加一下速,再執行一次看看:
def worker(name): print('%s: %s start...' % (time.strftime('%X'), name)) time.sleep(2 if name == '張三' else 1) print('%s: %s done.' % (time.strftime('%X'), name))
注:多執行幾次,你會發現,明明是t1.start() 再 t2.start(),為何回顯總是 李四 start 在前。那是因為阻塞的進程,執行過程中是不會回顯的,它需要執行完畢后才會一起回顯。因為總是 李四 done 先,所以回顯示就總是 李四 start 在前
溫馨提示:測試完記得把 worker 函數改回原來的樣子,下面例子是以原版的 worker 為基礎的
六、多進程不阻塞
def 多進程不阻塞(): t1 = multiprocessing.Process(target=worker, args=('張三',)) t2 = multiprocessing.Process(target=worker, args=('李四',)) t1.start() t2.start() print('Finished')
運行結果:
解釋: 參考上面單進程不阻塞↑,不阻塞的情況下,也是會等待全部進程任務執行完成才結束程序的。多進程有一定的並發現象
七、守護進程阻塞
def 守護進程阻塞(): t1 = multiprocessing.Process(daemon=True, target=worker, args=('張三',)) t2 = multiprocessing.Process(daemon=True, target=worker, args=('李四',)) t1.start() t2.start() t1.join() t2.join() print('Finished')
運行結果:
解釋:這里看着上面的普通進程沒有什么區別的,具體的區別看下面↓↓↓
八、守護進程不阻塞
def 守護進程不阻塞(): t1 = multiprocessing.Process(daemon=True, target=worker, args=('張三',)) t2 = multiprocessing.Process(daemon=True, target=worker, args=('李四',)) t1.start() t2.start() print('Finished')
運行結果:
解釋:進程任務都還沒執行,主程序就執行完畢,結束了。這是守護進程(后台進程)的一個特點:不阻塞的情況下,后台進程(守護進程)會在主程序結束的時候自動死掉
九、守護進程不阻塞但主進程比較晚結束
def 守護進程不阻塞但主進程比較晚結束(): t1 = multiprocessing.Process(daemon=True, target=worker, args=('張三',)) t2 = multiprocessing.Process(daemon=True, target=worker, args=('李四',)) t1.start() t2.start() time.sleep(5) print('Finished')
運行結果:
解釋:這里的主進程並沒有等子(守護)進程,只是主進程耗時比子進程還要久,子進程先執行完畢了
十、進程池阻塞
def 進程池阻塞(): with multiprocessing.Pool(processes=3) as pp: pp.apply(worker, ('張三',)) pp.apply(worker, ('李四',)) pp.close() pp.join() print('Finished')
運行結果:
解釋:運行結果基本和“多進程的正確阻塞”基本一樣,但又有一點不同,詳情請看下面 “十二” 點~
十一、進程池不阻塞
def 進程池不阻塞(): with multiprocessing.Pool(processes=3) as pp: pp.apply(worker, ('張三',)) pp.apply(worker, ('李四',)) pp.close() print('Finished')
運行結果:
解釋:這不阻塞的效果感覺和阻塞的效果並沒有什么區別
十二、進程池的奇怪現象
認真觀察一下“十、進程池阻塞”和“十一、進程池不阻塞”,還有一個現象,不管是阻塞還是不阻塞,就上面的例子而言,李四居然要等張三done之后才能start,它們不應該同時start,同時done的嗎??
對比一下上面“五、多進程的正確阻塞”的運行結果 和 “十一、進程池不阻塞”的結果:
與
解釋:普通的多進程 start 是一起的(並發,單任務不阻塞),done的話就看任務的實際耗時。進程池的話,使用apply添加任務的時候,會自動阻塞任務,一個任務完成,再才執行下一個apply的任務。只想阻塞進程池,不要阻塞單任務的話,可以使用 apply_async 方法,詳情請看下面↓↓↓
十三、進程池異步阻塞
def 進程池異步阻塞(): with multiprocessing.Pool(processes=3) as pp: pp.apply_async(worker, ('張三',)) pp.apply_async(worker, ('李四',)) pp.close() pp.join() print('Finished')
運行結果:
解釋,這下就真的和“多進程的正確阻塞”一樣的,不會阻塞單任務
十四、進程池異步不阻塞
def 進程池異步不阻塞(): with multiprocessing.Pool(processes=3) as pp: pp.apply_async(worker, ('張三',)) pp.apply_async(worker, ('李四',)) pp.close() print('Finished')
運行結果:
解釋:異步不阻塞的話,就會和守護進程一樣,主程序不會等待進程任務完成,會直接結束程序
十五、進程池同步並發不阻塞
上面說的apply是同步提交任務,apply_async是異步提交任務。apply是會阻塞的,順序執行任務。那有沒有同步又並發的方法呢?答案是有的,可以用map方法:
def 進程池同步並發不阻塞(): with multiprocessing.Pool(processes=3) as pp: pp.map(worker, ['張三', '李四']) pp.close() print('Finished')
運行結果:
解釋:提供給map方法一個函數和參數列表,它會迭代參數列表,並發執行函數~
需要注意的是,map和apply一樣,是同步且阻塞的,多次調用map方法會和多次調用apply一樣,會阻塞,需要等待第一次map任務執行完畢才會執行下面的map
def 進程池同步並發阻塞(): with multiprocessing.Pool(processes=3) as pp: pp.map(worker, ['張三', '李四']) pp.map(worker, ['王五', '趙六']) pp.close() print('Finished')
運行結果:
本文鏈接:https://www.cnblogs.com/tujia/p/13686684.html
完。