python多进程multiprocessing——spawn启动方式解析

下面我来详细讲解这段代码以及"spawn"到底是什么。

1. 代码逐行解析

python 复制代码
import multiprocessing

# 检查当前的进程启动方式是否为 "spawn"
if multiprocessing.get_start_method() != "spawn":
    # 如果不是,则强制将其设置为 "spawn"
    multiprocessing.set_start_method("spawn", force=True)
  • multiprocessing.get_start_method(): 获取当前系统默认创建新进程的方式。
  • multiprocessing.set_start_method("spawn", force=True) : 将创建新进程的方式设置为 spawnforce=True 表示即使之前已经设置过(或有默认值),也要强行覆盖。

2. 什么是 spawn

在 Python 中,创建一个新的子进程主要有三种模式:forkspawnforkserver

spawn 是其中一种最"干净"但启动较慢的模式。

形象的比喻:
  • Fork (克隆) :这是 Linux/Unix 的默认模式。就像把父进程这个人瞬间"复印"了一份。
    • 优点:速度极快,子进程继承了父进程的所有内存和资源。
    • 缺点:如果父进程已经很乱了(比如已经开启了多线程,或者持有了某些锁),克隆出来的子进程可能会带着这些"坏毛病",导致死锁或程序崩溃。
  • Spawn (新生) :这是 Windows 和 macOS 的默认模式。就像按照说明书重新雇佣了一个新员工。
    • 优点非常安全和干净。子进程会启动一个新的 Python 解释器,只加载必要的资源。它不会继承父进程混乱的内存状态或锁。
    • 缺点启动速度慢。因为要重新初始化 Python 环境和导入模块。

3. 为什么要用这段代码?

通常在以下几种情况下,你会看到这段代码:

  1. 跨平台兼容性

    • Linux 默认用 fork,Windows 默认用 spawn。为了保证代码在 Linux 和 Windows 上行为一致,开发者会强制指定为 spawn
  2. 深度学习(PyTorch/TensorFlow)与 CUDA

    • 这是最常见的原因。如果你在使用 PyTorch 进行 GPU 训练,CUDA 运行时不支持 fork
    • 如果在已经初始化了 CUDA 上下文的主进程中使用 fork 创建子进程,程序会报错或卡死。因此,PyTorch 的 DataLoader 等组件通常要求使用 spawn
  3. 多线程与多进程混用

    • 如果你的主程序中已经启动了多个线程 ,然后再尝试 fork 一个进程 ,非常容易导致死锁(因为 fork 会复制锁的状态,但不会复制持有锁的线程)。使用 spawn 可以避免这个问题。

4. 使用 spawn 的注意事项

如果你强制使用了 spawn,你的代码必须遵守一个严格的规则:

所有的启动代码必须放在 if __name__ == '__main__': 保护块之下。

因为 spawn 会重新导入你的脚本文件来初始化子进程。如果你不加这个保护块,子进程在导入文件时,又会尝试启动新的孙子进程,导致无限递归循环报错。

错误示例:

python 复制代码
import multiprocessing
multiprocessing.set_start_method("spawn", force=True)

def func():
    print("hello")

# 这里会报错,因为子进程import时会再次执行这行
p = multiprocessing.Process(target=func) 
p.start()

正确示例:

python 复制代码
import multiprocessing

def func():
    print("hello")

if __name__ == '__main__':
    # 必须放在这里面
    multiprocessing.set_start_method("spawn", force=True)
    p = multiprocessing.Process(target=func)
    p.start()
    p.join()

总结

这段代码是为了确保多进程程序的稳定性和跨平台兼容性 ,特别是在涉及 GPU 计算或复杂多线程环境时,通过牺牲一点点启动速度(使用 spawn),换取更安全、干净的进程运行环境。

相关推荐
n***271934 分钟前
JAVA (Springboot) i18n国际化语言配置
java·spring boot·python
家家小迷弟44 分钟前
docker容器内部安装python和numpy的方法
python·docker·numpy
小石头 100861 小时前
【Java】String类(超级详细!!!)
java·开发语言·算法
conkl1 小时前
Python中的鸭子类型:理解动态类型的力量
开发语言·python·动态·鸭子类型·动态类型规划
小小8程序员1 小时前
swift的inout的用法
开发语言·ios·swift
故事挺秃然1 小时前
Python异步(Asyncio)(一)
服务器·网络·python
大飞记Python1 小时前
【2025全攻略】PyCharm专业版 / 社区版如何打开.db 数据库文件
数据库·python·sql·pycharm
祈澈菇凉1 小时前
Next.js 零基础开发博客后台管理系统教程(一):环境搭建与项目初始化
开发语言·javascript·ecmascript
wjs20241 小时前
Go 语言切片(Slice)
开发语言