簡體   English   中英

在終端和 Django 或 Flask 的代碼模塊中使用 python 多處理池

[英]Using python multiprocessing Pool in the terminal and in code modules for Django or Flask

在 python 中使用 multiprocessing.Pool 和以下代碼時,有一些奇怪的行為。

from multiprocessing import Pool
p = Pool(3)
def f(x): return x
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
    try: print(t.get(timeout=1))
    except Exception: pass

我收到以下錯誤三次(池中的每個線程一個),並打印“3”到“19”:

AttributeError: 'module' object has no attribute 'f'

前三個 apply_async 調用永遠不會返回。

同時,如果我嘗試:

from multiprocessing import Pool
p = Pool(3)
def f(x): print(x)
p.map(f, range(20))

我得到 AttributeError 3 次,shell 打印“6”到“19”,然后掛起並且不能被 [Ctrl] + [C] 殺死

多處理文檔有以下內容:

此包中的功能要求模塊可由子模塊導入。

這是什么意思?

澄清一下,我正在終端中運行代碼來測試功能,但最終我希望能夠將其放入 Web 服務器的模塊中。 你如何在 python 終端和代碼模塊中正確使用 multiprocessing.Pool ?

這意味着池必須在定義要在其上運行的函數之后初始化。 if __name__ == "__main__": blocks 中使用池在您編寫獨立腳本時有效,但這在較大的代碼庫或服務器代碼(例如 Django 或 Flask 項目)中是不可能的。 因此,如果您嘗試在其中之一中使用池,請確保遵循以下指南中的說明:

  1. 盡可能在函數內部初始化池。 如果必須在全局范圍內初始化它們,請在模塊底部進行。
  2. 不要在全局范圍內調用 Pool 的方法。

或者,如果您只需要更好的 I/O 並行性(如數據庫訪問或網絡調用),您可以省去所有這些麻煩並使用線程池而不是進程池。 這涉及完全沒有記錄的:

from multiprocessing.pool import ThreadPool

它的接口與 Pool 的接口完全相同,但由於它使用線程而不是進程,因此它沒有使用進程池所做的任何警告,唯一的缺點是您無法獲得真正的代碼執行並行性,只是阻塞 I/O 中的並行性。


必須在定義要在其上運行的函數之后初始化池

python 文檔中難以理解的文本意味着在定義池時,池中的線程會導入周圍的模塊。 在 python 終端的情況下,這意味着您迄今為止運行的所有代碼。

因此,您要在池中使用的任何函數都必須在池初始化之前定義 模塊中的代碼和終端中的代碼都是如此。 問題中的代碼的以下修改將正常工作:

from multiprocessing import Pool
def f(x): return x  # FIRST
p = Pool(3) # SECOND
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
    try: print(t.get(timeout=1))
    except Exception: pass

要么

from multiprocessing import Pool
def f(x): print(x)  # FIRST
p = Pool(3) # SECOND
p.map(f, range(20))

很好,我的意思是在 Unix 上很好。 Windows 有它自己的問題,我不會在這里討論。


在模塊中使用池的注意事項

但是等等,還有更多(在你想導入到別處的模塊中使用池)!

如果在函數內部定義池,則沒有問題。 但是,如果您在模塊中使用 Pool 對象作為全局變量,則必須在頁面底部而不是頂部定義它 盡管這與大多數好的代碼風格背道而馳,但它是功能所必需的。 使用在頁面頂部聲明的池的方法是僅將其與從其他模塊導入的函數一起使用,如下所示:

from multiprocessing import Pool
from other_module import f
p = Pool(3)
p.map(f, range(20))

從另一個模塊導入預先配置的池是非常可怕的,因為導入必須在你想在它上面運行的任何東西之后進行,如下所示:

### module.py ###
from multiprocessing import Pool
POOL = Pool(5)

### module2.py ###
def f(x):
    # Some function
from module import POOL
POOL.map(f, range(10))

其次,如果您在要導入的模塊的全局范圍內的池上運行任何內容,系統就會掛起 即這不起作用

### module.py ###
from multiprocessing import Pool
def f(x): return x
p = Pool(1)
print(p.map(f, range(5)))

### module2.py ###
import module

但是,這確實有效,只要沒有導入 module2:

### module.py ###
from multiprocessing import Pool

def f(x): return x
p = Pool(1)
def run_pool(): print(p.map(f, range(5)))

### module2.py ###
import module
module.run_pool()

現在,這背后的原因只是更奇怪,並且可能與問題中的代碼只吐出一次屬性錯誤的原因有關,之后似乎可以正確執行代碼。 似乎池線程(至少具有一定的可靠性)在執行后重新加載模塊中的代碼。

要在線程池上執行的函數必須在創建池時已經定義。

這應該有效:

from multiprocessing import Pool
def f(x): print(x)
if __name__ == '__main__':
    p = Pool(3)
    p.map(f, range(20))

原因是(至少在具有fork系統上)當您創建池時,工作人員是通過對當前進程進行分叉來創建的。 因此,如果此時尚未定義目標函數,則工作人員將無法調用它。

在 windows 上有點不同,因為 windows 沒有fork 這里啟動了新的工作進程並導入了主模塊。 這就是為什么在 Windows 上使用if __name__ == '__main__'來保護正在執行的代碼很重要。 否則,每個新工作人員將重新執行代碼,從而無限地產生新進程,從而使程序(或系統)崩潰。

此錯誤還有另一個可能的來源。 運行示例代碼時出現此錯誤。

來源是盡管正確安裝了 multiprocessing,但我的系統上沒有安裝 C++ 編譯器,pip 在嘗試更新 multiprocessing 時通知了我。 因此可能值得檢查編譯器是否已安裝。

暫無
暫無

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

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