首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >多进程CUDA初始化的问题 Cannot re-initialize CUDA in forked subprocess

多进程CUDA初始化的问题 Cannot re-initialize CUDA in forked subprocess

作者头像
Michael阿明
发布2026-03-25 13:58:13
发布2026-03-25 13:58:13
830
举报

目录

  • 1. fork 多进程
  • 2. spawn多进程
  • 3. fork 和 spawn 的差异
    • 3.1 底层机制
    • 3.2 内存与状态继承
    • 3.3 与 CUDA / GPU 的兼容性
    • 3.4 Python 和跨平台行为
  • 4. 现象解释

背景:在多实例算法服务的时候,出现了 cuda 初始化错误

代码语言:javascript
复制
File "/usr/local/lib/python3.12/dist-packages/torch/cuda/__init__.py", line 398, in _lazy_init
    raise RuntimeError(
RuntimeError: Cannot re-initialize CUDA in forked subprocess. 
To use CUDA with multiprocessing, you must use the 'spawn' start method

先看两个例子

1. fork 多进程

代码语言:javascript
复制
# bad_multi_fork.py
import torch
import torch.nn as nn
import multiprocessing as mp

def worker(rank):
    print(f"[Worker {rank}] Starting...")
    x = torch.randn(5).cuda()  # 触发 CUDA 初始化
    print(f"[Worker {rank}] Tensor on {x.device}")

if __name__ == "__main__":
    # 主进程初始化 CUDA(关键!)
    _ = torch.tensor([1]).cuda()  # 或者加载模型
    print("[Main] CUDA initialized in main process.")

    processes = []
    for i in range(3):
        # 默认使用 'fork'(Linux)
        p = mp.Process(target=worker, args=(i,))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

上面这个例子会报错,主进程触发了 CUDA 初始化,fork 出来的子进程中创建 tensor 的时候报错了

如果把 _ = torch.tensor([1]).cuda() 这句去掉,那么主进程就没有 CUDA 初始化的操作,子进程不会报错

2. spawn多进程

代码语言:javascript
复制
# good_multi_spawn.py
import torch
import torch.nn as nn
import multiprocessing as mp

def worker(rank):
    print(f"[Worker {rank}] Starting...")
    x = torch.randn(5).cuda()
    print(f"[Worker {rank}] Tensor on {x.device}")

if __name__ == "__main__":
    # 主进程可以安全使用 CUDA
    main_tensor = torch.tensor([1]).cuda()
    print(f"[Main] Main tensor on {main_tensor.device}")

    # 显式使用 'spawn'
    ctx = mp.get_context("spawn")
    processes = []
    for i in range(3):
        p = ctx.Process(target=worker, args=(i,))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

    print("[Main] All workers finished.")

改成 spawn 方式创建子进程,不会报错

3. fork 和 spawn 的差异

spawnfork 是 Python(以及 Unix-like 系统)中 多进程(multiprocessing)的两种不同启动方式,它们在底层机制、内存共享、安全性、兼容性等方面有根本差异。 尤其在涉及 GPU(CUDA)多线程复杂库(如 PyTorch、TensorFlow) 时,选择错误的启动方法会导致程序崩溃或未定义行为。

3.1 底层机制

方式

机制

描述

fork

复制进程

调用 Unix fork() 系统调用,复制父进程的整个内存空间(包括代码、堆、栈、文件描述符等)创建子进程。子进程从 fork() 之后的代码继续执行。

spawn

启动新进程

启动一个全新的 Python 解释器进程,被 spawn 出的新进程拥有独立的内存地址空间,与父进程隔离。父子进程之间的变量、堆栈等默认不共享,修改不会互相影响。

3.2 内存与状态继承

方面

fork

spawn

内存

子进程拥有父进程内存的完整副本(写时复制,Copy-on-Write)

子进程从干净状态开始,不继承父进程的任何运行时状态(变量、对象、锁、线程等)

全局变量

继承父进程的值(修改不影响父进程)

重新执行模块,重新初始化所有全局变量

3.3 与 CUDA / GPU 的兼容性

启动方式

是否支持 CUDA

原因

fork

❌ 不安全(官方禁止)

CUDA runtime 不支持 fork()。一旦父进程初始化 CUDA,子进程继承的 CUDA 上下文是无效的,任何 GPU 操作会崩溃。

spawn

✅ 安全支持

子进程干净启动,独立初始化 CUDA,符合 NVIDIA 要求。

3.4 Python 和跨平台行为

平台

默认启动方法

说明

Linux / macOS

fork

传统 Unix 行为,速度快

Windows

spawn

Windows 没有 fork(),只能用 spawn

macOS(新版本)

推荐 spawn

使用 spawn 可让代码在 Windows / Linux / macOS 上行为一致

4. 现象解释

  • CUDA 上下文(Context)是进程级别的,每个进程使用CUDA时,都会触发 初始化
  • spawn 父子进程间 cuda 状态是隔离的,没有问题,不会出现二次初始化
  • fork 父子进程之间有 cuda 状态的继承不要在 CUDA 初始化后使用 fork()

上面 fork 的示例中

  • 第一次调用 cuInit()(由 PyTorch 在 .cuda() 等操作中触发)后,CUDA runtime 会在主进程的地址空间中记录“已初始化”。
  • 如果你 fork() 一个已经初始化 CUDA 的进程,子进程会复制这份“已初始化”状态,后面子进程使用 CUDA 的时候又会去初始化,导致二次初始化,报错

注释主进程的 cuda 操作后:

  • 主进程的 CUDA 状态是 “未初始化”
  • fork() 出的每个子进程都继承了“未初始化”状态
  • 当子进程第一次调用 .cuda() 时,各自独立调用 cuInit()这是合法的!
  • 因为每个子进程是独立的进程(有独立的 PID 和地址空间),CUDA runtime 允许每个进程初始化一次。

所以:“重复初始化”是指 同一个进程 多次初始化(不允许),而不是 多个独立进程 各自初始化(完全允许)

from https://michael.blog.csdn.net/

不对的地方,欢迎指教,感谢!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Michael阿明 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. fork 多进程
  • 2. spawn多进程
  • 3. fork 和 spawn 的差异
    • 3.1 底层机制
    • 3.2 内存与状态继承
    • 3.3 与 CUDA / GPU 的兼容性
    • 3.4 Python 和跨平台行为
  • 4. 现象解释
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档