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

启动多协程为何出现竞态条件?值异常及执行顺序疑问

协程多线程环境下变量计数错误的原因解析

一、为什么变量x的结果不正确?

核心问题是并发修改引发的竞态条件。++x看似是单一操作,但底层会拆成三个独立步骤:

  1. 读取当前x的数值
  2. 对读取到的值执行加1操作
  3. 将加1后的结果写回x

当使用Dispatchers.Default时,这些协程会被分配到线程池的多个线程中并行执行。比如两个协程同时执行第一步,都读到x=0,接着各自加1得到1,最后都把1写回x——这就导致两次增量操作只让x增加了1,而非预期的2。一万次这类操作累积下来,最终x的值自然会远小于10000。

二、协程的“顺序”到底指什么?

你说的“协程按顺序创建”是对的:repeat(10_000)会依次创建每一个协程,这个创建过程在runBlocking所在线程是串行执行的。但创建协程≠立即执行协程代码块。

每个创建好的协程会被放入Dispatchers.Default的调度队列,等待线程池中的空闲线程调度执行。线程池的调度是无序的,哪个协程先执行、由哪个线程执行,完全由调度策略决定。所以协程的创建是顺序的,但执行是并发且无序的,多个协程的代码块可能在不同线程同时运行,或交替抢占CPU执行,这就给竞态条件创造了发生的机会。

三、如何修复这个问题?

有几种常见的解决方式:

  • 使用原子类:把var x = 0替换为val x = AtomicInteger(0),用x.incrementAndGet()代替++x,原子操作能保证读、改、写的整个过程不可分割。
  • 使用互斥锁:用Mutex保护增量操作,确保同一时间只有一个协程能执行++x:
    fun main() = runBlocking {
        var x = 0
        val mutex = Mutex()
        repeat(10_000) {
            launch(Dispatchers.Default) {
                mutex.withLock {
                    ++x
                }
            }
        }
        delay(1.seconds)
        println(x)
    }
    
  • 串行执行增量:如果不需要并行,可将所有增量操作放到同一个协程中,或用channel串行处理任务,从根源避免并发修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:46:04