Python多进程get_context('spawn')运行报FileNotFoundError问题
问题根因
默认Pool偶发卡死的原因
Ubuntu环境下Python 3.6的multiprocessing默认使用fork方式启动子进程:该模式直接复制父进程内存空间生成子进程,无需重新导入主模块,但存在固有缺陷——如果父进程中存在未释放的锁、活跃线程、未同步的IO句柄状态,复制到子进程后极易触发死锁。传入的列表数据量越大,任务执行周期越长,触发状态冲突的概率越高,就会表现为程序偶发卡死无响应。
spawn模式抛出文件不存在错误的原因
spawn模式启动子进程的逻辑与fork完全不同:它会启动一个全新的Python解释器实例,通过重新导入主模块的方式加载目标函数、上下文变量。
你在Python交互式控制台(REPL)中执行代码时,主模块的路径是虚拟的<input>标识,并不对应磁盘上的实体文件,spawn生成的子进程尝试按照该路径加载主模块时,自然会抛出FileNotFoundError,错误栈中路径末尾的<input>就是该问题的直接标识。
可落地方案
根据使用场景选择对应方案即可:
- 生产环境正式运行时,不要在交互式控制台执行spawn模式的多进程代码:将执行逻辑写入实体
.py文件,添加if __name__ == "__main__":入口保护,直接运行脚本即可正常执行。参考代码如下:
from multiprocessing import get_context def multi(itr): return {itr: [{f'test{itr}'}]} def test_parallel(): list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] with get_context("spawn").Pool() as _pool: res = _pool.map(multi, list1) return res if __name__ == "__main__": test_parallel()
- 如果需要在交互式环境调试多进程逻辑,可将启动方式替换为
forkserver:该模式会预先启动一个单线程的干净服务进程,后续所有工作进程都从该服务进程fork生成,既规避了默认fork模式复制多线程父进程状态导致的死锁问题,也不需要全量重新导入主模块,不会触发找不到<input>文件的错误。仅需将进程池初始化代码替换为get_context("forkserver").Pool()即可。 - 小数据量临时调试可以继续使用默认fork模式,但生产环境处理大批量任务时不推荐使用,死锁触发概率会随任务规模上升明显提高。
注意:使用spawn或forkserver模式时,多进程执行的目标函数必须定义在模块顶层,可被正常导入,不能定义在函数内部、类的私有作用域或交互式环境的临时作用域中,否则子进程加载时会找不到对应函数定义抛出异常。
内容的提问来源于stack exchange,提问作者Rahman
相关产品推荐
相关产品推荐

