必须加if __name__ == '__main__',否则Windows下DataLoader的num_workers>0会因spawn机制导致子进程重复执行顶层代码而无限递归崩溃。

必须加 if __name__ == '__main__',否则 Windows 下任何 DataLoader 的 num_workers > 0 都会崩溃。
为什么 RuntimeError: DataLoader worker (pid(s)) exited unexpectedly 一定出现在 Windows?
因为 Windows 不支持 fork,Python 多进程只能用 spawn:子进程启动时会重新导入整个 .py 文件,从头执行所有顶层代码。如果 DataLoader 创建、train() 调用、甚至 print() 都写在模块顶层,子进程就会重复执行——触发无限 spawn 或找不到函数定义。
常见错误模式包括:
- 脚本末尾直接调用
train(),没包进函数 - 在 Jupyter Notebook 的 cell 里直接跑
multiprocessing相关代码(__name__是'__console__',不是'__main__') - 自定义
Dataset类里用了未序列化的对象(如 OpenCV VideoCapture、未关闭的文件句柄)
if __name__ == '__main__' 怎么写才真正生效?
不是“加了就行”,要确保所有多进程相关逻辑都严格包裹在该判断内,且不能有任何顶层执行语句干扰子进程初始化。
立即学习“Python免费学习笔记(深入)”;
正确结构示例:
import torch from torch.utils.data import DataLoader, TensorDataset <p>class MyDataset(TensorDataset): def <strong>init</strong>(self, <em>args): super().<strong>init</strong>(</em>args)</p><h1>✅ 安全:初始化只在主模块导入时执行一次</h1><p>def main(): dataset = MyDataset(torch.randn(1000, 3), torch.randint(0, 2, (1000,))) loader = DataLoader(dataset, batch_size=32, num_workers=4) # ✅ 在函数内创建 for x, y in loader: pass # 训练逻辑</p><p>if <strong>name</strong> == '<strong>main</strong>': main() # ✅ 唯一入口,子进程不会执行这行
容易被忽略的点:
-
torch.multiprocessing.set_start_method('spawn', force=True)在某些旧环境或混合 CUDA 场景下仍需显式加在if __name__ == '__main__'开头 - Spyder / VS Code Python 插件调试时,若直接右键“运行 Python 文件”,可能绕过保护;建议终端执行
python train.py - 使用
torch.compile或DDP时,if __name__ == '__main__'仍是前提,否则 DDP 初始化会与 DataLoader 冲突
为什么设 num_workers=0 能“临时”跑通?
它绕过了 spawn 机制,退化为单进程同步加载:数据预处理和模型训练串行执行,不涉及跨进程状态传递,自然避开 CUDA 上下文继承失败、pickle 失败、全局变量重入等问题。
但代价明显:
- GPU 利用率骤降(实测常低于 50%),尤其当
__getitem__含图像解码、几何变换等 CPU 密集操作时 - 验证阶段更容易暴露问题——因为验证通常无梯度计算,CPU 成为瓶颈更突出
- 无法 scale 到大 batch 或复杂 pipeline,是掩盖问题,不是解决问题
真正关键的不是“能不能跑”,而是“有没有把多进程初始化逻辑彻底隔离”。Windows 下漏掉哪怕一个顶层 print、一个未封装的 DataLoader 实例,都可能让子进程卡死在导入阶段——而错误堆栈往往不显示源头,只报 worker exit,这点最易被忽略。


















