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

1 场景说明

在Python中,使用multiprocessing模块创建子进程时,将创建子进程的代码放在if __name__ == '__main__': 块之外,如下面代码:

python 复制代码
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 问题描述

代码执行过后,报错信息如下:

python 复制代码
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__':中

python 复制代码
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 设置启动方式

python 复制代码
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 启动方式比较

启动方式 spawn fork forkserver
描述 重新启动一个 Python 解释器,只继承主进程中运行进程所需的资源。不会继承父进程的内存状态。 直接复制父进程的所有资源(包括内存状态),创建子进程。 先启动一个服务器进程,之后每次创建子进程都从该服务器进程 fork。
支持操作系统 Windows、macOS Linux、Unix Linux、Unix
优点 安全性高,避免了父进程状态不一致的问题。 启动速度快,因为直接复制父进程状态。 避免了直接 fork 父进程可能导致的问题。 比 spawn 更快,因为服务器进程已经初始化。
缺点 启动速度较慢,因为需要重新导入模块和初始化环境。 可能导致资源冲突(如文件描述符、锁等)。 在 macOS 上,fork 可能会导致问题(尤其是涉及多线程时)。 需要额外的服务器进程。
适用场景 Windows 和 macOS(默认方式)。 需要避免共享父进程状态的场景。 Linux/Unix(默认方式)。 需要快速启动子进程且不涉及多线程的场景。 Linux/Unix。 需要频繁创建子进程且希望避免资源冲突的场景。
相关推荐
卷无止境2 小时前
RAGFlow 项目全景报告:从文档解析引擎到 Agent 原生数据库
后端·python
灿飞屋3 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
benchmark_cc3 小时前
单只股票五档盘口接入个人研究仓库:QuantDash 读取、校验与易错点
python·数据分析·pandas·量化交易·股票数据·quantdash
青少儿编程课堂5 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:中缀表达式求值解析
c++·python·算法·bfs·信息学竞赛
灿飞屋7 小时前
Kotlin与Java虚拟机四:函数
java·python·kotlin·安卓·android jetpack
一晌小贪欢8 小时前
Python基础:第 09 篇 · while 循环:重复直到不满足
开发语言·c++·python·数据可视化·面向对象·python办公
一条大祥脚9 小时前
CuTe 与 CuTe DSL:从 C++ 模板元编程到 Python DSL 的 GPU 编程演进
开发语言·c++·python·cutlass·cute·cute dsl
10年前端老司机10 小时前
Milvus 8种检索能力实践
数据库·python·agent
Sakura_j_h_10 小时前
从零写一个 MCP stdio Server:用 Python 校验博客文章格式
开发语言·python·自动化·ai工具·mcp·json-rpc
大草原的小灰灰10 小时前
LLM应用的标准开发框架LangChain(八)- 中间件
python·中间件·langchain