簡體   English   中英

多處理隊列已滿

[英]multiprocessing queue full

我正在使用 concurrent.futures 來實現多處理。 我收到 queue.Full 錯誤,這很奇怪,因為我只分配了 10 個作業。

A_list = [np.random.rand(2000, 2000) for i in range(10)]

with ProcessPoolExecutor() as pool:
    pool.map(np.linalg.svd, A_list)

錯誤:

Exception in thread Thread-9:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 921, in _bootstrap_inner
    self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 869, in run
    self._target(*self._args, **self._kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/concurrent/futures/process.py", line 251, in _queue_management_worker
    shutdown_worker()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/concurrent/futures/process.py", line 209, in shutdown_worker
    call_queue.put_nowait(None)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/queues.py", line 131, in put_nowait
    return self.put(obj, False)
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/queues.py", line 82, in put
    raise Full
queue.Full

簡答
我相信管道尺寸限制是根本原因。 除了將數據分解成更小的塊並迭代處理它們之外,您對此無能為力。 這意味着您可能需要找到一種新算法,該算法可以一次處理 2000x2000 數組的一小部分,以找到奇異值組合。

細節
讓我們馬上明白一件事:您正在處理大量信息。 僅僅因為您只處理十個項目並不意味着它是微不足道的。 這些項目中的每一個都是一個 2000x2000 的數組,其中包含 4,000,000 個浮點數,每個浮點數通常為 64 位,因此您會看到每個數組大約 244MB,以及在Numpy 的 ndarrays中標記的其他數據。

ProcessPoolExecutor通過啟動一個單獨的線程來管理工作進程。 管理線程使用multiprocesing.Queue將作業傳遞給工作線程,稱為_call_queue 這些multiprocessing.Queue實際上只是圍繞管道的花哨包裝器,您嘗試傳遞給工作人員的 ndarrays 可能太大而無法正確處理管道。

閱讀Python 問題 8426表明,即使您可以查找操作系統的一些標稱管道大小限制,也很難准確確定管道的大小。 變數太多,無法簡單化。 即使是將事物從隊列中取出的順序也會在底層管道中引發競爭條件,從而觸發奇怪的錯誤。

我懷疑您的一名工人正在從_call_queue獲取一個不完整或損壞的對象,因為該隊列的管道充滿了您的巨型對象。 該工作人員以不干凈的方式死亡,工作隊列管理器檢測到此故障,因此它放棄工作並告訴其余工作人員退出。 但是它通過將毒丸傳遞給_call_queue這一點,它仍然充滿了你的巨大 ndarrays 這就是為什么您會收到完整隊列異常 - 您的數據填滿了隊列,然后管理線程嘗試使用相同的隊列將控制消息傳遞給其他工作人員。

我認為這是一個典型的例子,說明在程序中的不同實體之間混合數據和控制流的潛在危險。 您的大數據不僅阻止了工作人員接收更多數據,還阻止了經理與工作人員的控制通信,因為他們使用相同的路徑。

我無法重現你的失敗,所以我不能確定所有這些都是正確的。 但是,您可以使此代碼與 200x200 數組(~2.5MB)一起使用這一事實似乎支持這一理論。 標稱管道大小限制似乎以 KB 或最多幾 MB 為單位,具體取決於操作系統和體系結構。 如此大量的數據可以通過管道這一事實並不令人驚訝,尤其是當您考慮到如果消費者持續接收數據時,並非所有 2.5MB 的數據都需要一次性真正裝入管道。 它建議了您可以通過管道連續獲取的數據量的合理上限。

我最近在調試通過管道發送各種 GB 數據的 python3.6 程序時偶然發現了這一點。 這就是我發現的(希望它可以節省別人的時間!)。

就像skrrgwasme所說的那樣,如果隊列管理器在發送毒丸時無法獲取信號量,則會引發隊列已滿錯誤。 對信號量的獲取調用是非阻塞的,它會導致管理器失敗(由於數據和控制流共享同一個Queue ,它無法發送“控制”命令)。 請注意,上面的鏈接是指 python 3.6.0

現在我想知道為什么我的隊列管理器會發送毒丸。 一定有其他的失敗! 顯然發生了一些異常(在其他一些子進程中?在父進程中?),並且隊列管理器試圖清理和關閉所有子進程。 在這一點上,我有興趣找到這個根本原因。

調試根本原因

我最初嘗試記錄子進程中的所有異常,但顯然那里沒有發生顯式錯誤。 第 3895 期

請注意,當 unpickle 結果失敗時, multiprocessing.Pool 也會被破壞。

py36 中的多處理模塊似乎已損壞,因為它無法正確捕獲和處理序列化錯誤。

不幸的是,由於時間限制,我沒有設法自己復制和驗證問題,而是更喜歡跳轉到操作點和更好的編程實踐(不要通過管道發送所有數據:)。 這里有幾個想法:

  1. 嘗試腌制應該通過管道運行的數據。 由於我的數據量很大(數百 GB)和時間限制,我無法找到哪些記錄是不可序列化的。
  2. 將調試器放入python3.6並打印原始異常。

動作要點

  1. 如果可能,重構您的程序以減少通過管道發送的數據。

  2. 閱讀問題 3895后,問題似乎出自酸洗錯誤。 另一種(和良好的編程習慣)可能是使用不同的方式傳輸數據。 例如,可以讓子進程寫入文件並將路徑返回到父進程(這只是一個小字符串,可能是幾個字節)。

  3. 等待未來的python版本。 顯然,這是在問題 3895上下文中在 python 版本標簽 v3.7.0b3 上修復的。 Full異常將在shutdown_worker內部處理 撰寫本文時 Python 的當前維護版本為 3.6.5

暫無
暫無

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

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