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,并警告消息被正确地发出

相关问题