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


完。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM