设置set_start_method为spawn时创建Pool可访问Queue的方法
问题根源
spawn进程启动模式和默认fork模式的运行逻辑存在本质差异:
fork模式直接复制父进程的全部内存状态,子进程天然可以访问父进程中定义的全局变量spawn模式会启动一个干净的全新Python解释器进程,重新加载执行目标脚本,仅if __name__ == '__main__'判断块外的代码会在子进程加载阶段运行。示例代码中b = mp.Queue()写在判断块内部,子进程加载脚本时不会执行这行定义,因此抛出NameError: name 'b' is not defined。
注意:不要直接把
b = mp.Queue()移到判断块外修复问题。spawn模式下每个子进程加载脚本时都会独立执行这行代码,各自生成完全独立的Queue实例,主进程、不同子进程持有的队列互不连通,根本无法实现跨进程通信。
解决方案
方案1:通过Pool初始化器注入全局变量(推荐,全启动模式兼容)
利用mp.Pool的initializer和initargs参数,在子进程启动阶段将主进程创建的共享Queue注入子进程全局命名空间,Windows、macOS默认spawn环境下都能稳定运行:
import multiprocessing as mp def f(_): print(b) def _pool_init(queue): global b b = queue def main(): print("main") print(b) print("running f") f(0) with mp.Pool(2, initializer=_pool_init, initargs=(b,)) as pool: pool.map(f, list(range(2))) if __name__ == '__main__': mp.set_start_method('spawn') b = mp.Queue() main()
该方案下所有子进程拿到的都是主进程创建的同一个共享Queue,不会出现资源重复初始化、通信链路不通的问题。
方案2:作为任务参数直接传递
如果不想使用全局变量,可以直接把Queue作为参数传入任务函数,适合逻辑简单的场景:
import multiprocessing as mp from functools import partial def f(queue, _): print(queue) def main(): queue = mp.Queue() print("main") print(queue) print("running f") f(queue, 0) # 用partial固定queue参数,适配pool.map的单参数任务要求 task = partial(f, queue) with mp.Pool(2) as pool: pool.map(task, list(range(2))) if __name__ == '__main__': mp.set_start_method('spawn') main()
multiprocessing会自动对传入的Queue做序列化和代理封装,子进程拿到的队列对象和主进程原生Queue完全连通,可以正常执行put/get等操作。
内容的提问来源于stack exchange,提问作者Fortunato
相关产品推荐
相关产品推荐

