1️⃣ 启动方式差异(spawn vs fork)
Linux 默认:fork
- 父进程创建子进程时,会直接 fork 一个子进程
- 子进程继承父进程的内存空间(写时复制)
- 全部全局变量、模块导入、打开的文件描述符都被继承
优点:
启动快
可以直接继承父进程状态,无需 pickle(对象序列化)
缺点:
对有线程或者某些资源的程序可能会有隐蔽问题(例如锁、数据库连接)
Windows 默认:spawn
- 父进程不会 fork,而是新启动一个 Python 解释器
- 子进程会重新执行主模块 (__main__)
- 需要 pickle 父进程中传给子进程的对象
特点:
安全、干净,但性能开销大
需要保护主入口:if __name__ == "__main__":
全局变量不会自动继承
2️⃣ main 保护
在 Windows 上,如果不加:
if __name__ == "__main__":
# 这里放 multiprocessing 相关代码
很容易无限递归启动子进程,导致爆炸。
示例(错误写法):
from multiprocessing import Process
def worker():
print("child")
p = Process(target=worker)
p.start()
p.join()
Windows 上直接运行会无限 spawn,因为子进程也会执行整个脚本,再次创建 Process。
3️⃣ 全局变量继承
Linux (fork):
x = 10
def worker():
print(x) # 子进程直接访问全局变量 x = 10
Windows (spawn):
x = 10
def worker():
print(x) # 子进程可能访问不到全局变量,必须 pickle 或在 __main__ 初始化
4️⃣ 共享资源的差异
资源类型 Linux (fork) Windows (spawn)
打开的文件描述符 继承 不继承
套接字 (socket) 继承 不继承
全局变量 继承 不继承
Logger / StreamHandlers 有风险 安全
隐蔽陷阱:在 Linux 上你可能直接用全局 logger,而在 Windows 上 logger 可能在子进程中丢失。
5️⃣ Queue、Pipe、Manager 的行为
Linux:可以直接用父进程对象传给子进程(因为 fork 继承了内存)
Windows:必须 pickle(序列化),性能慢,某些对象不能序列化(如 lambda, open 文件对象)。
6️⃣ 进程池(Pool)差异
Linux:
from multiprocessing import Pool
def f(x): return x*x
with Pool(4) as p:
print(p.map(f, [1,2,3]))
Windows:
if __name__ == "__main__":
from multiprocessing import Pool
def f(x): return x*x
with Pool(4) as p:
print(p.map(f, [1,2,3]))
必须在 __main__ 保护下,否则报错或无限递归。
7️⃣ 性能差异
Linux fork:创建进程快,内存写时复制
Windows spawn:每次都启动解释器,pickle 参数,慢 5~10 倍不止
隐蔽陷阱:跨平台性能差异可能让你觉得程序在 Windows “卡死”,其实只是 spawn 开销。
8️⃣ 调试和 IDE 注意事项
Linux:IDE 调试通常不会影响 fork
Windows:在 IDE 或 Jupyter Notebook 中运行 multiprocessing,必须注意 if __name__ == "__main__",否则容易无限 spawn 或报错。
总结
启动方式不同:Linux fork,Windows spawn → 导致全局变量继承、文件描述符、logger 等行为差异
Windows 必须 __main__ 保护
对象必须可 pickle(Queue, Pipe, Pool 参数)
性能差异明显:Windows spawn 慢,Linux fork 快
跨平台调试要注意 IDE 环境
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70050070/viewspace-3117956/ ,如需转载,请注明出处,否则将追究法律责任。