1

我正在使用Python(2.7)和pymongo(3.3),我需要產生一個子進程異步運行一個作業。不幸的是,pymongo並不像上面描述的here(我需要在生成子進程之前與db進行交互)分叉安全。在Python中產生一個沒有分叉的進程

我跑使用subprocess.Popen(與shell設置爲True然後False)和multiprocessing.Process實驗。據我可以告訴他們都分叉父進程創建子進程,但只有multiprocessing.Process導致pymongo打印其警告,它已檢測到分叉進程。

我在想這樣做的pythonic方式是什麼。看來,也許os.system會爲我做,但subprocess被描述爲os.system的預期替代,所以我想知道我是否錯過了一些東西。

回答

4

我想你誤會;由於PyMongo的文檔警告你,單個MongoClient不是fork-safe,你可以解釋爲PyMongo禁止你的整個程序創建子進程。

任何單個MongoClient不是叉安全的,這意味着你不能分叉之前創建它,分叉後,使用相同的MongoClient對象。總體來說,在程序中使用PyMongo,或者在分叉之前使用一個MongoClient,而在之後使用不同的MongoClient都是安全的。

這就是爲什麼subprocess.Popen沒問題:你分叉,然後執行exec(用子進程中的另一個代替你的程序),因此你不可能在子進程中使用同一個MongoClient。

引述PyMongo FAQ

在Unix系統的多處理模塊使用叉產生進程()。在fork()中使用MongoClient實例時必須小心。具體來說,MongoClient的實例不能從父進程複製到子進程。相反,父進程和每個子進程必須創建他們自己的MongoClient實例。例如:

# Each process creates its own instance of MongoClient. 
def func(): 
    db = pymongo.MongoClient().mydb 
    # Do something with db. 

proc = multiprocessing.Process(target=func) 
proc.start() 

永遠不要這樣做:MongoClient的

client = pymongo.MongoClient() 

# Each child process attempts to copy a global MongoClient 
# created in the parent process. Never do this. 
def func(): 
    db = client.mydb 
    # Do something with db. 

proc = multiprocessing.Process(target=func) 
proc.start() 

實例從父進程複製具有僵局的子進程中的高概率,由於固有的fork(),線程和鎖之間的不兼容性。如果發生這種死鎖,PyMongo會嘗試發出警告。

+0

啊哈有道理,我也很好奇在子進程中繼承文件描述符(特別是套接字句柄)的潛在副作用,但我猜Popen的close_fds參數地址也是 – nonagon

+0

PyMongo使用FD_CLOEXEC創建其套接字,因此無論您是否傳遞close_fds,這些描述符都會關閉。 –

0

如果不在* NIX環境中使用fork,絕對沒有辦法產生新進程。事實上,系統內部運行的所有進程都是init進程的一個分支。 從我所瞭解的情況來看,在叉子之間使用安全的pymongo手柄並不安全。您可以嘗試產生線程而不是進程,或者從子分支中打開不同的句柄給pymongo。

2

如果你能夠移動到Python 3.4或更高版本,你可以在使用pymongo之前,將你的multiprocessing start method設置爲'forkserver'。這立即叉叉服務器進程,並且所有將來使用的叉指派叉服務器,而不是您的主進程。因此,一旦fork服務器設置完畢,你的主進程可以使用pymongo,fork服務器不會使用它,所以它不會有重新生成問題。

可悲的是,在3.4只添加啓動方法,所以它不是2.7的選擇,但如果別人有這個問題,它可能是對他們有用。

+0

是的,這正是我所希望的。我現在看到pymongo會在fork/exec之後工作,但是分叉​​一些新鮮的不相關的進程感覺就像是一種更清潔的方式來產生子進程給我(也許我的windows根目前正在顯示)。不幸的是,python 2-> 3感覺像我們的代碼庫已經有幾年了:( – nonagon

5

無法派生安全並不意味着你不能調用fork ......它只是意味着該子進程不應該使用任何繼承PyMongo實例。當您使用subprocess.Popen時,新分叉的子幾乎立即調用exec以被shell實例(shell = True)或所需的可執行文件(shell = False)替代。所以從PyMongo的角度來看它是安全的。

在當你調用multiprocessing.Process相反,孩子確實是家長的副本,並保持其PyMongo實例。因此,它是不安全的在這方面使用PyMongo,並警告消息被正確地發出

相關問題