1. 程式人生 > 實用技巧 >python 程序(process)阻塞

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


完。