必须用 if name == '__main__': 包裹 DataLoader 创建和迭代逻辑,否则子进程重复执行顶层代码易导致 CUDA 初始化卡死;VSCode(Windows)中 num_workers>0 基本不可用,应设为 0;__getitem__ 中禁用非 fork-safe 操作;pin_memory=True 需注意 /dev/shm 容量。

if <strong>name</strong> == '<strong>main</strong>': 是硬性起点,不加它,其他优化全白搭。
必须用 if __name__ == '__main__': 包裹 DataLoader 创建和迭代逻辑
spawn 启动方式下,子进程会重新导入主模块;若顶层代码里有 torch.device('cuda')、model = MyNet() 或任何副作用操作,子进程就会重复执行,大概率卡在 CUDA 上下文初始化或资源锁上。
常见错误现象包括:程序停在 for batch in dataloader:、无报错、CPU 占用低、GPU 显存没变化。
即使你把数据加载逻辑封装进函数,只要没用这个 guard,子进程仍会执行模块顶层语句。
Jupyter 用户不能直接用 if __name__ == '__main__':,得改用:if 'get_ipython' in globals(): + 显式调用训练函数。
num_workers > 0 在 VSCode(Windows)中基本不可用
VSCode 的 Python 启动机制会让子进程反序列化失败,报错类似 AttributeError: Can't get attribute 'MyDataset' on <module '__main__' from ''> 或静默退出。
实测全版本(Windows 10/11 + VSCode 1.89+ + PyTorch 2.3+)唯一可靠解法是设 num_workers=0。
这不是权宜之计——多数情况下性能损失被高估;真正瓶颈常来自 VSCode 终端输出、调试器 hook 或扩展干扰。
验证是否为 IDE 问题:在 CMD/PowerShell 中直接运行脚本,对比行为;若 CMD 正常,就别折腾 spawn 或 mp_context。
避免在 Dataset.__getitem__ 中触发非 fork-safe 操作
子进程无法继承父进程的 CUDA 上下文、GUI 线程、OpenCV 内部线程池或 logging 配置;一旦触发,大概率阻塞或崩溃。
务必确保:
- 不在 __getitem__ 中调用 torch.cuda.*、cv2.imread(换成 PIL.Image.open)、matplotlib.pyplot 或 h5py.File
- 若必须用 OpenCV,提前在 __init__ 中加 cv2.setNumThreads(0)
- 所有预处理逻辑只做纯 CPU 操作:PIL 加载、numpy 转换、torch.tensor() 构造(device 默认为 'cpu')
- collate_fn 运行在主进程,但若它引用了不可序列化的对象(如 open 的文件句柄),也会让 worker 初始化失败。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
pin_memory=True 和共享内存容量容易被忽略
pin_memory=True 会预分配 CUDA pinned memory,依赖 /dev/shm;默认仅 64MB,大图 + 多 worker + 大 batch_size 很快撑爆,报 OSError: unable to open shared memory object。
临时扩容:sudo mount -t tmpfs -o size=2G tmpfs /dev/shm
更稳妥做法:设 pin_memory=False + prefetch_factor=2,让 worker 直接返回 Python 对象,绕过共享内存拷贝。
另外,pin_memory=True 在 CPU-only 环境或调试阶段反而加剧 fork 冲突,建议初期关闭。
真正的难点不在“怎么开多进程”,而在“怎么不让子进程干不该干的事”——从模块导入那一刻起,每个顶层语句、每个隐式 import、每个全局状态初始化,都可能成为死锁伏笔。

















