1 场景说明
在Python中,使用multiprocessing模块创建子进程时,将创建子进程的代码放在if __name__ == '__main__': 块之外,如下面代码:
- import multiprocessing
- import time
- def test_func(name):
- print(f"子进程 {name} 开始运行")
- time.sleep(2) # 模拟任务执行
- print(f"子进程 {name} 结束运行")
- # 创建子进程
- p = multiprocessing.Process(target=test_func, args=("1",))
- # 启动子进程
- p.start()
- # 等待子进程结束
- p.join()
- print("主进程结束")
复制代码 2 问题描述
代码执行过后,报错信息如下:
- raise RuntimeError('''
- RuntimeError:
- An attempt has been made to start a new process before the
- current process has finished its bootstrapping phase.
- This probably means that you are not using fork to start your
- child processes and you have forgotten to use the proper idiom
- in the main module:
- if __name__ == '__main__':
- freeze_support()
- ...
- The "freeze_support()" line can be omitted if the program
- is not going to be frozen to produce an executable.
- To fix this issue, refer to the "Safe importing of main module"
- section in https://docs.python.org/3/library/multiprocessing.html
复制代码 3 原因分析
在 Python 中,使用 multiprocessing 模块创建子进程时,必须将创建子进程的代码放在if __name__ == '__main__':块中。这是由于在 Windows 和 macOS 上,Python 使用spawn方式启动子进程,这种方式会重新导入主模块。如果不将代码放在 if __name__ == '__main__': 块中,会导致子进程重新执行主模块的代码,从而引发递归创建子进程的问题。
4 解决方案
将创建子进程的代码块放到if __name__ == '__main__':中
- import multiprocessing
- import time
- def test_func(name):
- print(f"子进程 {name} 开始运行")
- time.sleep(2) # 模拟任务执行
- print(f"子进程 {name} 结束运行")
- if __name__ == '__main__':
- # 创建子进程
- p = multiprocessing.Process(target=test_func, args=("1",))
- # 启动子进程
- p.start()
- # 等待子进程结束
- p.join()
- print("主进程结束")
复制代码 5 Multiprocessing模块中子进程启动方式
5.2 启动方式说明
在 Python 的 multiprocessing 模块中,子进程的启动方式重要有三种:spawn、fork 和 forkserver。不同的利用系统默认使用不同的启动方式,而这些方式的行为和实用场景也有所不同。
5.3 默认启动方式
Windows: 默认使用 spawn。
macOS(Python 3.8+): 默认使用 spawn。
Linux/Unix: 默认使用 fork。
5.4 设置启动方式
- import multiprocessing
- def worker():
- print("子进程运行")
- if __name__ == '__main__':
- # 设置启动方式,可选spawn、fork、forkserver
- multiprocessing.set_start_method('spawn')
- # 如果设置成fork,则会抛异常ValueError: cannot find context for 'fork'
- # 创建子进程
- p = multiprocessing.Process(target=worker)
- p.start()
- p.join()
复制代码 5.5 如何选择启动方式
Windows/macOS: 默认使用 spawn,无需更改。
Linux/Unix:
如果必要快速启动且不涉及多线程,使用 fork。
如果必要制止资源冲突,使用 forkserver 或 spawn。
5.6 留意事项
5.6.1 多线程与 fork
在 Linux 上,如果父进程中有多线程,使用 fork 可能会导致死锁或资源冲突。在这种情况下,建议使用spawn或 forkserver。
5.6.2 跨平台兼容性
如果代码必要在多个平台上运行,建议使用spawn,由于它是 Windows 和 macOS 的默认方式。
5.7 启动方式比力
启动方式spawnforkforkserver描述重新启动一个 Python 表明器,只继承主进程中运行进程所需的资源。不会继承父进程的内存状态。直接复制父进程的所有资源(包罗内存状态),创建子进程。先启动一个服务器进程,之后每次创建子进程都从该服务器进程 fork。支持利用系统Windows、macOSLinux、UnixLinux、Unix优点安全性高,制止了父进程状态不一致的问题。启动速度快,由于直接复制父进程状态。制止了直接 fork 父进程可能导致的问题。
比 spawn 更快,由于服务器进程已经初始化。缺点启动速度较慢,由于必要重新导入模块和初始化情况。可能导致资源冲突(如文件描述符、锁等)。
在 macOS 上,fork 可能会导致问题(尤其是涉及多线程时)。必要额外的服务器进程。实用场景Windows 和 macOS(默认方式)。
必要制止共享父进程状态的场景。Linux/Unix(默认方式)。
必要快速启动子进程且不涉及多线程的场景。Linux/Unix。
必要频繁创建子进程且盼望制止资源冲突的场景。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |