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
相关产品推荐
相关产品推荐

