如果我需要共享multiprocessing.Queue
或multiprocessing.Manager
(或任何其他同步原语),那么通过在全局(模块)级别定义它们是否有任何区别,将它们作为参数传递给在不同进程中执行的函数?
例如,我可以想象三种可能的方式可以共享队列:
# works fine on both Windows and Linux
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
def main():
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
if __name__ == '__main__':
main()
VS
# works fine on Linux, hangs on Windows
from multiprocessing import Process, Queue
q = Queue()
def f():
q.put([42, None, 'hello'])
def main():
p = Process(target=f)
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
if __name__ == '__main__':
main()
VS
# works fine on Linux, NameError on Windows
from multiprocessing import Process, Queue
def f():
q.put([42, None, 'hello'])
def main():
p = Process(target=f)
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
if __name__ == '__main__':
q = Queue()
main()
哪种方法正确?我从我的实验中猜测它只是第一个,但是想确认它的正式情况(不仅适用于Queue
而且适用于Manager
和其他类似的对象)。
答案 0 :(得分:1)
明确地将资源传递给子进程
在使用fork start方法的Unix上,子进程可以使用全局资源在父进程中创建的共享资源。但是,最好将对象作为参数传递给子进程的构造函数。
除了使代码(可能)与Windows和其他启动方法兼容之外,这还确保只要子进程仍处于活动状态,对象就不会在父进程中进行垃圾回收。如果在父进程中对对象进行垃圾回收时释放某些资源,这可能很重要。
问题是spawn / forkserver(Windows仅支持spawn)在引擎盖下工作的方式。它不是用内存和文件解析器克隆父进程,而是从地面创建一个新进程。然后它加载一个新的Python解释器,传递模块以导入并启动它。这显然意味着您的全局变量将是一个全新的Queue而不是父变量。
另一个含义是,要传递给新进程的对象必须是pickleable,因为它们将通过管道传递。
答案 1 :(得分:0)
唯一可移植的解决方案是通过将它们作为参数传递来共享Queue()
和Manager().*
个对象 - 从不作为全局变量。原因是在Windows上,所有全局变量都将通过字面上运行模块重新创建(而不是复制)代码从头开始(very little information实际上是从父进程复制到子进程);所以会创建一个全新的Queue()
,当然(没有一些不受欢迎和混乱的魔法),它可能无法连接到父进程中的Queue()
。
我的理解是,将Queue()
等作为参数传递是没有缺点的;我找不到任何人想要使用全局变量的非便携式解决方案的原因,但当然我可能错了。