spawn 是跨平台的「干净启动」,fork 是 Unix 的「快速复制」。嵌套进程、跨平台开发首选 spawn。
核心差异
| 维度 | fork(Unix 专属) | spawn(跨平台) |
|---|
| 创建机制 | 复制父进程地址空间(COW) | 启动新解释器,重新导入脚本 |
| 启动速度 | 极快 | 较慢 |
| 资源继承 | 继承所有资源 | 仅继承必要资源 |
| 线程安全 | 父进程有活跃线程时易死锁 | 天然线程安全 |
| 嵌套进程 | 易出问题 | 完美支持 |
| 跨平台 | 差(Windows 不支持) | 好(全平台通用) |
fork 原理
- 调用
fork() 复制父进程整个地址空间 - 子进程从
fork() 位置开始执行 - 写时复制(COW):修改数据时才真正拷贝内存
spawn 原理
- 启动全新 Python 解释器
- 子进程重新导入当前脚本
- 必须用
if __name__ == '__main__': 保护主逻辑
实践验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| import multiprocessing
import os
GLOBAL_VAR = "父进程初始化"
def worker():
print(f"子进程: {GLOBAL_VAR}")
if __name__ == '__main__':
# fork:继承全局变量
multiprocessing.set_start_method('fork')
p = multiprocessing.Process(target=worker)
p.start(); p.join()
# spawn:重新初始化全局变量
multiprocessing.set_start_method('spawn', force=True)
p = multiprocessing.Process(target=worker)
p.start(); p.join()
|
嵌套进程
1
2
3
4
5
6
7
8
9
10
11
12
13
| def nested_worker():
print("嵌套子进程启动")
def parent_worker():
p = multiprocessing.Process(target=nested_worker)
p.start(); p.join()
if __name__ == '__main__':
# fork:大概率死锁
# spawn:稳定运行
multiprocessing.set_start_method('spawn')
p = multiprocessing.Process(target=parent_worker)
p.start(); p.join()
|
避坑指南
| 问题 | 解决方案 |
|---|
Windows 未加 if __name__ == '__main__': | 所有进程启动逻辑放保护块内 |
| fork 导致死锁 | 改用 spawn |
| spawn 启动慢 | 用进程池复用 |
set_start_method 重复调用 | 放脚本开头,或用 force=True |
选择建议
| 场景 | 推荐 |
|---|
| 纯 Linux/macOS、无嵌套、追求性能 | fork |
| 跨平台、嵌套进程、有活跃线程 | spawn |
| 数据处理、进程池 | spawn |
| 简单脚本 | spawn(减少适配成本) |
Comments