python 程序(process)阻塞
本文連結: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
完。