You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

joblib.Parallel切换threading/multiprocessing后端结果异常问题

问题根因

两个后端的内存模型差异是核心原因:

  • threading后端基于多线程实现,所有工作线程和主进程共享同一块内存地址空间,传入的out_list就是主进程中的原始对象,子线程修改列表元素的操作会直接作用在原对象上,因此主进程最终打印能拿到正确值。
  • multiprocessing后端基于多进程实现,每个工作进程是独立的操作系统进程,拥有完全独立的内存空间。任务分发时,out_list会被序列化后拷贝一份传给每个子进程,子进程修改的只是自己内存里的列表副本,主进程中的原始out_list从未被修改,因此最终打印全是初始化的None。

你观察到的三类表现对应逻辑:

  • 运行速度大幅提升:多进程可以绕过Python GIL全局解释器锁,CPU密集型场景下性能远高于受GIL限制的多线程,属于正常表现。
  • 任务函数内print输出正确:子进程修改的是自己持有的列表副本,副本内存储的计算值本身是正确的,因此打印结果正常。
  • 最终print全为None:主进程的原列表没有收到任何子进程的修改,始终保留初始化的None值。
修复方案

不要通过传入可变对象、在任务内部修改的方式收集结果——Parallel本身会按任务执行顺序收集所有被装饰函数的返回值,直接利用返回值组装结果是多进程并行的标准写法,没有额外的进程通信开销,性能最优。

修复后的可运行代码:

from joblib import Parallel, delayed

def E_th(i, tt):
    calc_res = tt + i
    print(calc_res)
    return tt, calc_res  # 返回索引和对应计算结果,方便后续组装列表


if __name__ == "__main__":
    time_range = range(0, 10)
    for i in range(0, 2):
        out_list = [None] * len(time_range)
        # 直接收集Parallel返回的所有任务结果,无需传入out_list
        task_results = Parallel(n_jobs=64, backend='multiprocessing')(
            delayed(E_th)(i, tt) for tt in time_range
        )
        # 按索引将结果填充到out_list
        for idx, val in task_results:
            out_list[idx] = val
        print(out_list)

如果有强需求必须在多进程间共享可变列表,需要使用multiprocessing模块提供的跨进程共享结构(比如Manager().list()、共享内存Array),但这类方案会引入进程间序列化、通信的额外开销,性能远低于直接收集返回值的方案,非必要不推荐使用。

内容的提问来源于stack exchange,提问作者Billy Matlock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:15:25