簡體   English   中英

Python 3 隊列為空前調用join時多處理隊列死鎖

[英]Python 3 Multiprocessing queue deadlock when calling join before the queue is empty

我對了解 python 3 中multiprocessing模塊中的隊列有疑問

這就是他們在編程指南中所說的:

請記住,將項目放入隊列的進程將在終止之前等待,直到所有緩沖的項目都由“饋送”線程饋送到底層 pipe。 (子進程可以調用隊列的 Queue.cancel_join_thread 方法來避免這種行為。)

這意味着無論何時使用隊列,您都需要確保所有已放入隊列的項目最終都會在進程加入之前被刪除。 否則,您無法確定將項目放入隊列的進程將終止。 還要記住,非守護進程將自動加入。

一個會死鎖的例子如下:

  from multiprocessing import Process, Queue def f(q): q.put('X' * 1000000) if __name__ == '__main__': queue = Queue() p = Process(target=f, args=(queue,)) p.start() p.join() # 這個死鎖 obj = queue.get()

此處的解決方法是交換最后兩行(或簡單地刪除 p.join() 行)。

顯然, queue.get()不應該在join()之后調用。

但是,有一些使用隊列的示例,其中getjoin后調用,例如:

import multiprocessing as mp
import random
import string

# define a example function
def rand_string(length, output):
    """ Generates a random string of numbers, lower- and uppercase chars. """
    rand_str = ''.join(random.choice(
                string.ascii_lowercase
                + string.ascii_uppercase
                + string.digits)
    for i in range(length))
        output.put(rand_str)

 if __name__ == "__main__":
     # Define an output queue
     output = mp.Queue()

     # Setup a list of processes that we want to run
     processes = [mp.Process(target=rand_string, args=(5, output))
                    for x in range(2)]

     # Run processes
    for p in processes:
        p.start()

    # Exit the completed processes
    for p in processes:
        p.join()

    # Get process results from the output queue
    results = [output.get() for p in processes]

    print(results)

我已經運行了這個程序並且它可以工作(也作為 StackOverFlow 問題Python 3 - 多處理 - Queue.get() 不響應的解決方案發布)。

有人可以幫我理解死鎖的規則是什么嗎?

允許在進程之間傳輸數據的多處理隊列實現依賴於標准操作系統管道。

操作系統管道不是無限長的,因此在put()操作期間, put()數據排隊的進程可能會在操作系統中被阻塞,直到某個其他進程使用get()從隊列中檢索數據。

對於少量數據,例如您的示例中的數據,主進程可以join()所有產生的子進程,然后獲取數據。 這通常效果很好,但不能擴展,也不清楚何時會崩潰。

但它肯定會因大量數據而崩潰。 子進程將在put()阻塞,等待主進程使用get()從隊列中刪除一些數據,但主進程在join()阻塞,等待子進程完成。 這會導致死鎖。

這是一個用戶遇到這個確切問題的示例。 我在那里的答案中發布了一些代碼,幫助他解決了問題。

在從共享隊列中獲取所有消息之前,不要在進程對象上調用join()

我使用以下解決方法允許進程在處理所有結果之前退出:

results = []
while True:
    try:
        result = resultQueue.get(False, 0.01)
        results.append(result)
    except queue.Empty:
        pass
    allExited = True
    for t in processes:
        if t.exitcode is None:
            allExited = False
            break
    if allExited & resultQueue.empty():
        break

它可以縮短,但我把它留得更久,以便新手更清楚。

這里resultQueuemultiprocess.Queue ,將其與共享multiprocess.Process對象。 在此代碼塊之后,您將獲得包含隊列中所有消息的result數組。

問題是接收消息的隊列管道的輸入緩沖區可能會變滿導致寫入者無限阻塞,直到有足夠的空間來接收下一條消息。 所以你有三種方法可以避免阻塞:

  • 增加multiprocessing.connection.BUFFER大小(不太好)
  • 減少消息大小或其數量(不太好)
  • 收到消息后立即從隊列中獲取消息(好方法)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM