使用Multiprocessing模块创建子进程,必要放到__main__中

瑞星  金牌会员 | 2025-3-19 15:13:48 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 957|帖子 957|积分 2871

1 场景说明

在Python中,使用multiprocessing模块创建子进程时,将创建子进程的代码放在if __name__ == '__main__': 块之外,如下面代码:
  1. import multiprocessing
  2. import time
  3. def test_func(name):
  4.     print(f"子进程 {name} 开始运行")
  5.     time.sleep(2)  # 模拟任务执行
  6.     print(f"子进程 {name} 结束运行")
  7. # 创建子进程
  8. p = multiprocessing.Process(target=test_func, args=("1",))
  9. # 启动子进程
  10. p.start()
  11. # 等待子进程结束
  12. p.join()
  13. print("主进程结束")
复制代码
2 问题描述

代码执行过后,报错信息如下:
  1. raise RuntimeError('''
  2. RuntimeError:
  3.         An attempt has been made to start a new process before the
  4.         current process has finished its bootstrapping phase.
  5.         This probably means that you are not using fork to start your
  6.         child processes and you have forgotten to use the proper idiom
  7.         in the main module:
  8.             if __name__ == '__main__':
  9.                 freeze_support()
  10.                 ...
  11.         The "freeze_support()" line can be omitted if the program
  12.         is not going to be frozen to produce an executable.
  13.         To fix this issue, refer to the "Safe importing of main module"
  14.         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__':中
  1. import multiprocessing
  2. import time
  3. def test_func(name):
  4.     print(f"子进程 {name} 开始运行")
  5.     time.sleep(2)  # 模拟任务执行
  6.     print(f"子进程 {name} 结束运行")
  7. if __name__ == '__main__':
  8.     # 创建子进程
  9.     p = multiprocessing.Process(target=test_func, args=("1",))
  10.     # 启动子进程
  11.     p.start()
  12.     # 等待子进程结束
  13.     p.join()
  14.     print("主进程结束")
复制代码
5 Multiprocessing模块中子进程启动方式

5.2 启动方式说明

在 Python 的 multiprocessing 模块中,子进程的启动方式重要有三种:spawn、fork 和 forkserver。不同的利用系统默认使用不同的启动方式,而这些方式的行为和实用场景也有所不同。
5.3 默认启动方式

Windows: 默认使用 spawn。
macOS(Python 3.8+): 默认使用 spawn。
Linux/Unix: 默认使用 fork。
5.4 设置启动方式

  1. import multiprocessing
  2. def worker():
  3.     print("子进程运行")
  4. if __name__ == '__main__':
  5.     # 设置启动方式,可选spawn、fork、forkserver
  6.     multiprocessing.set_start_method('spawn')
  7.     # 如果设置成fork,则会抛异常ValueError: cannot find context for 'fork'
  8.     # 创建子进程
  9.     p = multiprocessing.Process(target=worker)
  10.     p.start()
  11.     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企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

瑞星

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表