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

为何Julia多线程累加代码添加println后未出现竞态条件

问题本质解释

两段代码都存在竞态条件,第一段代码添加println后能得到1000只是测试规模下的巧合,并非真正实现了线程安全。

  • acc += 1本身是非原子操作:它的执行分为三个独立步骤:读取内存中acc的当前值、对值执行加1运算、将新值写回acc的内存地址。当两个线程同时完成第一步读取操作时,后续会各自计算加1后写回,彼此覆盖结果,最终总累加次数就会小于1000,这就是第二段代码的问题来源。
  • println操作大幅降低了竞态触发概率:
    1. Julia的标准输出流默认是线程安全的,内部内置了互斥锁,同一时间仅允许一个线程执行打印逻辑,相当于在累加操作前强制让线程串行执行了打印环节。
    2. 打印属于IO操作,耗时是纯内存算术运算的数千到数万倍,极大拉长了每个线程执行累加操作的时间窗口,多个线程同时读取acc值的概率被降到极低,在你测试的1000次循环规模下几乎不会触发冲突,所以每次运行都得到1000。
  • 注意:这不是线程安全的解决方案,如果将循环规模放大到1e6甚至更高,哪怕保留println语句,依然会出现最终acc小于循环次数的情况。
正确的线程安全累加实现

要彻底避免竞态条件,可以用以下两种常见方案:

  1. 使用原子变量保证累加操作的原子性
acc = Threads.Atomic{Int}(0)
Threads.@threads for i in 1:1000
    Threads.atomic_add!(acc, 1)
end
# 读取原子变量的值需要用 []
@assert acc[] == 1000
  1. 使用线程局部存储先各自累加,最后汇总结果
# 每个线程对应一个局部计数器,避免写冲突
local_counters = zeros(Int, Threads.nthreads())
Threads.@threads for i in 1:1000
    local_counters[Threads.threadid()] += 1
end
acc = sum(local_counters)
@assert acc == 1000

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 16:06:05