python多進程假死


結論:python多進程間用Queue通信時,如果子進程操作Queue滿了或者內容比較大的情況下,該子進程會阻塞等待取走Queue內容(如果Queue數據量比較少,不會等待),如果調用join,主進程將處於等待,等待子進程結束,造成死鎖

解決方式:在調用join前,及時把Queue的數據取出,而且Queue.get需要在join前

原理分析

模擬子進程阻塞:

  1.  
    from multiprocessing import Process, Queue
  2.  
     
  3.  
     
  4.  
    def fun(q):
  5.  
    num = 1000000000
  6.  
    q.put( '=' * num)
  7.  
    print( "done put")
  8.  
     
  9.  
     
  10.  
    if __name__ == '__main__':
  11.  
    queue = Queue()
  12.  
    p = Process(target=fun, args=(queue,))
  13.  
    p.start()
  14.  
    p.join()
  15.  
    print( "done")

原因分析:

  1.  
    #
  2.  
    # Queue type using a pipe, buffer and thread
  3.  
    #
  4.  
     
  5.  
    class Queue(object):

multiprocessing.Queue底層是基於Pipe構建,操作系統管道不是無限長,因此子進程在執行put()期間,處於阻塞,直到某些其他進程使用get()從隊列中取走數據。上例中,主進程等待子進程,打印不了done

 

而當隊列put數據比較少時,是沒有問題的,先打印done put,再打印done,但這樣寫法是有隱患,當put數據比較多時,就會阻塞

  1.  
    from multiprocessing import Process, Queue
  2.  
     
  3.  
     
  4.  
    def fun(q):
  5.  
    num = 1
  6.  
    q.put( '=' * num)
  7.  
    print( "done put")
  8.  
     
  9.  
     
  10.  
    if __name__ == '__main__':
  11.  
    queue = Queue()
  12.  
    p = Process(target=fun, args=(queue,))
  13.  
    p.start()
  14.  
    p.join()
  15.  
    print( "done")

正確的寫法

  1.  
    from multiprocessing import Process, Queue
  2.  
     
  3.  
     
  4.  
    def fun(q):
  5.  
    num = 1000000000
  6.  
    q.put( '=' * num)
  7.  
    print( "done put")
  8.  
     
  9.  
     
  10.  
    if __name__ == '__main__':
  11.  
    queue = Queue()
  12.  
    p = Process(target=fun, args=(queue,))
  13.  
    p.start()
  14.  
    queue.get()
  15.  
    p.join()
  16.  
    print( "done")

在join前面調用queue.get

 

注意!!!以下這樣寫法也是不對的,join要在queue.get前面,不然主進程等待子進程結束,而子進程等待隊列數據取走,造成死鎖

  1.  
    p = Process(target=fun, args=(queue,))
  2.  
    p.start()
  3.  
    p.join()
  4.  
    queue.get()

 

Python多線程補充

Python 是一門解釋型語言,它的執行是由解釋器來控制的。

GIL,全稱是 Global Interpreter Lock ,全局解釋鎖 ,專門給解釋器用

一般情況下在用戶態下是無法做到線程級別的時間片輪轉

但是 python 能做到!python 里,解釋器可以記錄每一個線程執行了多長時間——時間一到,就能夠切換到另一條線程。

GIL 就是拿來給線程加鎖的,當一個線程將要執行時,解釋器會把 GIL 鎖給這個線程,其他線程因為沒有鎖,是無法運行的。等到持有鎖線程阻塞或者運行 100 個字節碼,解釋器就會把鎖交給其他線程。

但是這個 GIL 鎖是全局(Global)的,也就導致即使是多核情況下,一次也只有一個線程能運行,從整體上看,整個程序是串行的。

python多線程應用

拿爬蟲程序來說吧,單個爬蟲總會花時間在下載網頁上,很多 CPU 時間就浪費掉了,提供 sleep 機制后,這些爬蟲可以在等待下載時釋放 GIL 鎖,把機會讓給其他爬蟲,這樣整體運行速度能夠得到大幅提升

也就是說 Python 的多線程適合 I/O 密集型的程序,但是對計算密集型程序就不那么友好了

對於計算密集型程序用多進程或者讓 python 調用 C 語言的代碼,在 C 語言里實現多線程


免責聲明!

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



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