DataLoader 一开 worker 就报 Cannot re-initialize CUDA,检查 Dataset 是否在上 GPU
CUDA fork 错误与普通主入口递归不同。历史复现是在 Dataset 中创建 CUDA 张量;让 worker 返回 CPU 数据,再由主进程搬到 GPU,并分别验证多进程读取和真实计算。
num_workers=0 可以读取数据,开多个 worker 后报:
Cannot re-initialize CUDA in forked subprocess.
PyTorch issue #77159的复现使用 PyTorch 1.11.0、CUDA 10.2 与 Python 3.7.13,Dataset 在 __getitem__ 中直接创建 CUDA 张量。这个版本信息属于原报告;下面示例按 PyTorch 2.8 的数据加载形态说明修正。
它与 Windows 的 bootstrapping 主入口错误不同。CUDA 运行时不能按普通 fork 方式继承已初始化的状态;多进程文档说明,确需 CUDA 子进程时要采用合适的启动方式与资源生命周期。仅换启动方法并不会替你设计跨进程 GPU 张量的所有权。
普通数据加载,worker 留在 CPU
检查 Dataset、transform、collate 和 worker 初始化函数,查找 .cuda()、.to("cuda") 或带 CUDA device 的张量构造。不只检查训练循环那一行。
下面是一个小脚本。它要求测试环境有可用 CUDA,GPU 运算只有简单乘法:
import torch
from torch.utils.data import Dataset, DataLoader
class CpuRows(Dataset):
def __len__(self):
return 8
def __getitem__(self, index):
return torch.tensor([float(index)], dtype=torch.float32)
def main():
loader = DataLoader(CpuRows(), batch_size=2, num_workers=2)
for batch in loader:
assert batch.device.type == "cpu"
gpu_batch = batch.to("cuda")
result = (gpu_batch * 2).cpu()
assert result.shape == batch.shape
if __name__ == "__main__":
main()
Dataset 类放在可导入的模块顶层,启动和消费循环留在主入口。原数据处理逻辑应保留;示例只是把 GPU 迁移放回拥有模型的主进程。
可先移除示例中的 GPU 两行,验证所有 CPU 批次能读完,再恢复实际 GPU 步骤。这样可以分开定位数据读取异常与 CUDA 计算异常。
num_workers=0 是诊断基线
单进程可以帮助读到原始数据异常,但它没有验证多进程修复。对真实数据集还要检查文件句柄、连接和局部函数的可序列化性;这些资源不能因 CUDA 错误消失就自动视为可共享。
确实需要让子进程执行 CUDA 时,应按 spawn 或 forkserver 的模型设计,分别创建相应资源,并处理发送者寿命、消费者退出与异常清理。框架、服务器或调度器可能自行管理进程,应用里的一次 set_start_method 不保证覆盖它们。
验收用多 worker 遍历代表性数据,确认批次数和样本数正确;在主进程完成小批计算,再恢复真实训练或推理。检查正常退出后没有残留 worker。不能只看到错误不再出现,就认定吞吐或显存使用已经改善。


