根本原因是 pytorch 默认 fork 启动方式复制了主进程的 cuda 上下文,而子进程无法使用该上下文,导致 gpu 操作阻塞;需显式调用 torch.multiprocessing.set_start_method('spawn') 并在子进程中独立加载模型到 cuda。

为什么 torch.multiprocessing 在模型推理时容易卡死?
根本原因是 PyTorch 默认启用的 fork 启动方式会复制主进程的 CUDA 上下文,而 CUDA 上下文在子进程中不可用或处于未初始化状态,导致调用 model.forward() 时阻塞在 GPU 操作上——尤其在 Linux + 多卡环境下更常见。这不是代码逻辑错误,而是启动方法与 CUDA 运行时的兼容性问题。
常见现象包括:子进程 CPU 占用 100% 但无输出、queue.get() 永远不返回、pool.map() 卡住不动。
- 只在使用
cuda设备时触发;cpu设备通常不受影响 -
spawn或forkserver启动方式可绕过该问题,但需显式设置 - PyTorch ≥ 1.9 后默认仍为
fork,不能依赖版本自动修复
如何强制使用 spawn 启动方式?
必须在 if __name__ == '__main__': 保护块内、任何 torch 模块导入之后、创建进程前调用 torch.multiprocessing.set_start_method('spawn')。顺序错或位置错都会失效。
示例关键片段:
if __name__ == '__main__':
import torch
torch.multiprocessing.set_start_method('spawn') # 必须在这儿,且只调用一次
model = torch.load('model.pth', map_location='cpu') # 注意:别在主进程加载 cuda 模型
with torch.multiprocessing.Pool(4) as pool:
results = pool.map(inference_fn, data_list)
- 不能在模块顶层或函数内部调用
set_start_method - 若已调用过(比如其他库提前触发了 multiprocessing),会抛出
RuntimeError: context has already been set - Windows/macOS 默认就是
spawn,但显式声明仍推荐,避免跨平台差异
为什么不能在主进程把模型加载到 cuda 后再传给子进程?
因为 spawn 方式下,子进程是全新 Python 解释器,无法继承主进程的 CUDA 上下文和张量设备状态。直接传递 model.to('cuda') 对象会导致子进程尝试复用无效句柄,轻则报错 CUDA error: invalid device ordinal,重则静默卡死。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- 正确做法:主进程只加载模型权重(用
map_location='cpu'),让每个子进程各自调用.to(device) - 如果用
nn.DataParallel或torch.compile包装过模型,必须在子进程中重新构建,不能序列化传递 - 避免使用全局变量缓存模型——
spawn下子进程看不到主进程的全局状态
有没有更轻量、不依赖 torch.multiprocessing 的替代方案?
有。对纯推理场景,concurrent.futures.ProcessPoolExecutor 更可控,且不隐式依赖 PyTorch 的 multiprocessing 行为。它底层也用 spawn(Python 3.8+ macOS/Linux 默认),但绕开了 PyTorch 的上下文管理干扰。
关键区别:
- 不需要调用
torch.multiprocessing.set_start_method - 模型加载、设备绑定全部在 worker 函数内完成,隔离性更强
- 注意:仍要避免在
__main__外 import torch,否则可能触发提前初始化
示例结构:
def worker_inference(data_item):
import torch
model = load_model() # 内部加载并 to('cuda:0')
with torch.no_grad():
return model(data_item)
<p>if <strong>name</strong> == '<strong>main</strong>':
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(worker_inference, data_list))
</p>
多进程推理死锁不是模型写错了,而是启动方式和设备管理没对齐。最容易被忽略的是:即使用了 spawn,只要主进程提前把模型放到了 cuda,子进程就大概率卡在 cudaSynchronize 调用里,且不会报错。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










