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

Julia多线程循环执行函数写入CSV异常问题求助

解决Julia多线程循环仅生成最后一次迭代CSV文件的问题

问题重现

使用Threads.@threads并行化循环时,仅最后一次迭代的CSV文件被生成,其余迭代的文件均未正确写入。核心代码如下:

shapes = ["s", "linear", "exp", "expinv"]

Threads.@threads for i in 1:4
    P["SHAPE"] = shapes[i]
    P["DOMINANT"] = true
    f_dom, m_dom, t_dom = prop_plot(P)
    P["DOMINANT"] = false
    f_rec, m_rec, t_rec = prop_plot(P)

    CSV.write(string(filepath, "ML_f_dom_u0250r4872_", shapes[i],".csv"), f_dom)
    CSV.write(string(filepath, "ML_m_dom_u0250r4872_", shapes[i],".csv"), m_dom)
    CSV.write(string(filepath, "ML_t_dom_u0250r4872_", shapes[i],".csv"), t_dom)
    CSV.write(string(filepath, "ML_f_rec_u0250r4872_", shapes[i],".csv"), f_rec)
    CSV.write(string(filepath, "ML_m_rec_u0250r4872_", shapes[i],".csv"), m_rec)
    CSV.write(string(filepath, "ML_t_rec_u0250r4872_", shapes[i],".csv"), t_rec)
end

核心原因

问题并非来自I/O共享竞争,而是全局变量P的线程竞争:多个线程同时修改同一个全局字典P,导致线程间的参数互相覆盖。例如,线程1刚将P["SHAPE"]设为s,线程2立即将其改为linear,此时线程1调用prop_plot(P)时实际使用的是linear的参数,最终生成的文件内容与文件名不匹配,甚至因参数混乱导致计算失败、文件未写入。

可行解决方案

1. 为每个线程创建独立参数副本

在循环内复制全局字典P,修改副本而非原变量,彻底隔离线程间的参数:

shapes = ["s", "linear", "exp", "expinv"]

Threads.@threads for i in 1:4
    # 为当前线程创建独立的参数副本
    P_thread = deepcopy(P)
    P_thread["SHAPE"] = shapes[i]
    
    P_thread["DOMINANT"] = true
    f_dom, m_dom, t_dom = prop_plot(P_thread)
    P_thread["DOMINANT"] = false
    f_rec, m_rec, t_rec = prop_plot(P_thread)

    CSV.write(string(filepath, "ML_f_dom_u0250r4872_", shapes[i],".csv"), f_dom)
    CSV.write(string(filepath, "ML_m_dom_u0250r4872_", shapes[i],".csv"), m_dom)
    CSV.write(string(filepath, "ML_t_dom_u0250r4872_", shapes[i],".csv"), t_dom)
    CSV.write(string(filepath, "ML_f_rec_u0250r4872_", shapes[i],".csv"), f_rec)
    CSV.write(string(filepath, "ML_m_rec_u0250r4872_", shapes[i],".csv"), m_rec)
    CSV.write(string(filepath, "ML_t_rec_u0250r4872_", shapes[i],".csv"), t_rec)
end

2. 重构函数,避免全局变量依赖

将prop_plot修改为接收具体参数的函数,完全抛弃全局字典P,从根源上消除线程竞争:

shapes = ["s", "linear", "exp", "expinv"]

Threads.@threads for i in 1:4
    shape = shapes[i]
    # 直接传递参数给prop_plot
    f_dom, m_dom, t_dom = prop_plot(shape, true)
    f_rec, m_rec, t_rec = prop_plot(shape, false)

    CSV.write(string(filepath, "ML_f_dom_u0250r4872_", shape,".csv"), f_dom)
    CSV.write(string(filepath, "ML_m_dom_u0250r4872_", shape,".csv"), m_dom)
    CSV.write(string(filepath, "ML_t_dom_u0250r4872_", shape,".csv"), t_dom)
    CSV.write(string(filepath, "ML_f_rec_u0250r4872_", shape,".csv"), f_rec)
    CSV.write(string(filepath, "ML_m_rec_u0250r4872_", shape,".csv"), m_rec)
    CSV.write(string(filepath, "ML_t_rec_u0250r4872_", shape,".csv"), t_rec)
end

# 重构后的prop_plot示例
function prop_plot(shape::String, dominant::Bool)
    # 将原逻辑中使用P["SHAPE"]、P["DOMINANT"]的地方替换为传入的参数
    # ... 原有计算逻辑 ...
    return f, m, t
end

3. (可选)I/O操作加锁(针对极端场景)

如果确实存在文件系统层面的I/O竞争(如多个线程同时写入同一目录导致系统异常),可以用ReentrantLock对写入操作加锁,确保同一时间只有一个线程执行写入:

const csv_lock = ReentrantLock()

shapes = ["s", "linear", "exp", "expinv"]

Threads.@threads for i in 1:4
    # ... 前面的计算逻辑(先解决参数竞争问题) ...
    
    lock(csv_lock) do
        CSV.write(string(filepath, "ML_f_dom_u0250r4872_", shapes[i],".csv"), f_dom)
        CSV.write(string(filepath, "ML_m_dom_u0250r4872_", shapes[i],".csv"), m_dom)
        CSV.write(string(filepath, "ML_t_dom_u0250r4872_", shapes[i],".csv"), t_dom)
        CSV.write(string(filepath, "ML_f_rec_u0250r4872_", shapes[i],".csv"), f_rec)
        CSV.write(string(filepath, "ML_m_rec_u0250r4872_", shapes[i],".csv"), m_rec)
        CSV.write(string(filepath, "ML_t_rec_u0250r4872_", shapes[i],".csv"), t_rec)
    end
end

关于@sync/@async的说明

之前尝试@sync和@async无效,是因为@async默认是单线程异步任务(需配合Threads.@spawn才能实现多线程),且未解决核心的全局变量竞争问题,因此性能无法超越串行执行,问题也未得到解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:35:00