在 Python 中产生一个进程而不分叉
Spawn a process in Python without forking
我正在使用 Python (2.7) 和 pymongo (3.3),我需要为 运行 异步作业生成一个子进程。不幸的是,pymongo 并不像 here 所描述的那样是安全的(并且我需要在生成子进程之前与数据库进行交互)。
我 运行 使用 subprocess.Popen
(shell
设置为 True
,然后 False
)和 multiprocessing.Process
的实验。据我所知,他们都分叉父进程来创建子进程,但只有 multiprocessing.Process
导致 pymongo 打印它的警告,即它已检测到分叉进程。
我想知道这样做的 pythonic 方式是什么。似乎 os.system
可能会为我做,但 subprocess
被描述为 os.system
的预期替代品,所以我想知道我是否遗漏了什么。
如果您能够升级到 Python 3.4 或更高版本,则可以在使用 pymongo
之前将 multiprocessing
start method 设置为 'forkserver'
。这会立即分叉一个分叉服务器进程,并且以后所有对 multiprocessing
的使用都会分叉该分叉服务器,而不是您的主进程。因此,一旦设置了分叉服务器,您的主进程就可以使用 pymongo
,分叉服务器不会使用它,因此重新分叉不会有问题。
遗憾的是,启动方法仅在 3.4 中添加,因此它不是 2.7 的选项,但如果其他人遇到此问题,它可能对他们有用。
不安全并不意味着您不能调用 fork...它只是意味着 child 进程不应使用任何继承的 PyMongo 实例。当您使用 subprocess.Popen
时,新分叉的 child 几乎立即调用 exec
以替换为 shell 实例(shell = True)或所需的可执行文件( shell = 假)。所以从 PyMongo 的角度来看它是安全的。
相反,当您调用 multiprocessing.Process 时,child 确实是 parent 的副本,并且保留其 PyMongo 实例。因此在该上下文中使用 PyMongo 是不安全的,并且正确发出了警告消息
我想你误会了;由于 PyMongo 的文档警告您单个 MongoClient 不是分叉安全的,您将其解释为 PyMongo 禁止您的整个程序创建子进程。
任何单个 MongoClient 都不是分叉安全的,这意味着您不得在分叉前创建它并在分叉后使用相同的 MongoClient 对象。在您的程序中整体使用 PyMongo,或者在 fork 之前使用一个 MongoClient 并在 fork 之后使用另一个,都是安全的。
这就是 subprocess.Popen
没问题的原因:先 fork,然后执行(在子进程中用不同的程序替换您的程序),因此之后您不可能在子进程中使用相同的 MongoClient。
引用PyMongo FAQ:
On Unix systems the multiprocessing module spawns processes using fork(). Care must be taken when using instances of MongoClient with fork(). Specifically, instances of MongoClient must not be copied from a parent process to a child process. Instead, the parent process and each child process must create their own instances of MongoClient. For example:
# 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()
Never do this:
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()
Instances of MongoClient copied from the parent process have a high probability of deadlock in the child process due to inherent incompatibilities between fork(), threads, and locks. PyMongo will attempt to issue a warning if there is a chance of this deadlock occurring.
我正在使用 Python (2.7) 和 pymongo (3.3),我需要为 运行 异步作业生成一个子进程。不幸的是,pymongo 并不像 here 所描述的那样是安全的(并且我需要在生成子进程之前与数据库进行交互)。
我 运行 使用 subprocess.Popen
(shell
设置为 True
,然后 False
)和 multiprocessing.Process
的实验。据我所知,他们都分叉父进程来创建子进程,但只有 multiprocessing.Process
导致 pymongo 打印它的警告,即它已检测到分叉进程。
我想知道这样做的 pythonic 方式是什么。似乎 os.system
可能会为我做,但 subprocess
被描述为 os.system
的预期替代品,所以我想知道我是否遗漏了什么。
如果您能够升级到 Python 3.4 或更高版本,则可以在使用 pymongo
之前将 multiprocessing
start method 设置为 'forkserver'
。这会立即分叉一个分叉服务器进程,并且以后所有对 multiprocessing
的使用都会分叉该分叉服务器,而不是您的主进程。因此,一旦设置了分叉服务器,您的主进程就可以使用 pymongo
,分叉服务器不会使用它,因此重新分叉不会有问题。
遗憾的是,启动方法仅在 3.4 中添加,因此它不是 2.7 的选项,但如果其他人遇到此问题,它可能对他们有用。
不安全并不意味着您不能调用 fork...它只是意味着 child 进程不应使用任何继承的 PyMongo 实例。当您使用 subprocess.Popen
时,新分叉的 child 几乎立即调用 exec
以替换为 shell 实例(shell = True)或所需的可执行文件( shell = 假)。所以从 PyMongo 的角度来看它是安全的。
相反,当您调用 multiprocessing.Process 时,child 确实是 parent 的副本,并且保留其 PyMongo 实例。因此在该上下文中使用 PyMongo 是不安全的,并且正确发出了警告消息
我想你误会了;由于 PyMongo 的文档警告您单个 MongoClient 不是分叉安全的,您将其解释为 PyMongo 禁止您的整个程序创建子进程。
任何单个 MongoClient 都不是分叉安全的,这意味着您不得在分叉前创建它并在分叉后使用相同的 MongoClient 对象。在您的程序中整体使用 PyMongo,或者在 fork 之前使用一个 MongoClient 并在 fork 之后使用另一个,都是安全的。
这就是 subprocess.Popen
没问题的原因:先 fork,然后执行(在子进程中用不同的程序替换您的程序),因此之后您不可能在子进程中使用相同的 MongoClient。
引用PyMongo FAQ:
On Unix systems the multiprocessing module spawns processes using fork(). Care must be taken when using instances of MongoClient with fork(). Specifically, instances of MongoClient must not be copied from a parent process to a child process. Instead, the parent process and each child process must create their own instances of MongoClient. For example:
# 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()
Never do this:
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()
Instances of MongoClient copied from the parent process have a high probability of deadlock in the child process due to inherent incompatibilities between fork(), threads, and locks. PyMongo will attempt to issue a warning if there is a chance of this deadlock occurring.