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

单线程用importlib导入时scipy报字典迭代大小变化错误如何解决

问题根因分析

  • 你观测到的单线程只是Python层面的线程,PyTorch导入时会隐式启动大量C层面的原生工作线程(包括CUDA调度线程、OpenBLAS/MKL线性代数运算线程),这类线程不会出现在threading.enumerate()的返回结果中,但同样会参与GIL抢占。
  • AWS G4实例启动初期处于资源预热阶段,GPU驱动、容器资源调度、CUDA运行时的初始化工作尚未稳定,原生线程的GIL抢占时机波动极大,刚好在scipy 1.6.3执行list(globals().items())迭代全局字典视图的间隙,持有GIL修改了全局字典的内容,就会触发字典迭代时被修改的RuntimeError。
  • 运行时间越长复现概率越低的原因是:启动阶段的各类初始化工作完成后,原生后台线程的活跃度大幅下降,刚好命中scipy导入时机的概率自然降低。
  • 网上提到的提前加pairs = []的方案本质是歪打正着:提前在全局字典中新增了pairs键,减少了迭代过程中新增键的概率,并没有从根源解决问题。

可行解决方案

  • 方案1:调整导入时机
    不要在业务实例方法中做延迟导入,将torch、filterpy的导入逻辑放到模块顶部,或者在程序启动的初始化阶段提前触发一次导入,避开启动阶段的高波动调度窗口。
  • 方案2:增加导入重试逻辑
    对importlib.import_module的调用增加3~5次重试,捕获RuntimeError后间隔100ms左右重试即可,该问题属于偶发调度冲突,重试几乎可以100%规避。示例代码如下:
    import importlib
    import time
    def safe_import(module_name, retry=3, interval=0.1):
        for _ in range(retry):
            try:
                return importlib.import_module(module_name)
            except RuntimeError as e:
                if "dictionary changed size during iteration" not in str(e):
                    raise
                time.sleep(interval)
        return importlib.import_module(module_name)
    
    # 调用时替换原import逻辑
    torch = safe_import("torch")
    kalman = safe_import("filterpy.kalman")
    
  • 方案3:升级依赖版本
    scipy 1.7.0及以上版本已经重构了_discrete_distns.py中的全局字典遍历逻辑,从根源修复了该问题,升级scipy到1.7+版本即可彻底解决。
  • 方案4:减少原生后台线程数量
    在导入torch前设置环境变量限制线性代数库的线程数,降低GIL抢占概率:
    import os
    os.environ["OMP_NUM_THREADS"] = "1"
    os.environ["MKL_NUM_THREADS"] = "1"
    import torch
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 03:18:04