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

如何仅用Go原子操作实现无锁循环自增索引且避免竞态条件?

仅用Go原子操作实现带重置的自增逻辑

当然可以!完全不用锁,只靠Go的原子操作就能搞定这个需求。先说说你原来的示例代码为啥会有竞态问题:

你写的代码里,atomic.AddUint32(&index, 1)执行完之后,到atomic.CompareAndSwapUint32(&index, Max, 0)这一步之间,其他goroutine可能已经偷偷修改了index的值,导致CAS的判断条件失效,最后逻辑就乱了。

正确的原子实现方式

我们需要用CAS循环(Compare-And-Swap循环)来保证整个逻辑的原子性——毕竟单个原子操作没法完成“自增+判断重置”这一串动作,得通过循环尝试直到整个操作成功。

代码实现如下:

import "sync/atomic"

const Max = 8
var index uint32 // 改用uint32适配原子操作的类型要求

func add() uint32 {
    for {
        // 先原子加载当前的index值,确保拿到的是最新状态
        current := atomic.LoadUint32(&index)
        // 计算下一个值:自增后如果达到Max就重置为0
        next := current + 1
        if next >= Max {
            next = 0
        }
        // 尝试CAS操作:如果当前index还是我们读取的current,就替换成next
        if atomic.CompareAndSwapUint32(&index, current, next) {
            // CAS成功,说明整个操作原子完成,返回新值
            return next
        }
        // CAS失败,说明有其他goroutine修改了index,重新循环重试就行
    }
}

为啥这个实现是并发安全的?

  • 整个逻辑通过CAS循环把“读当前值→算新值→替换新值”绑定成了一个原子操作:只有当index的值和我们刚读取的current完全一致时,才会执行替换,避免了中间被其他goroutine篡改的可能。
  • 如果期间有其他goroutine修改了index,CAS会直接失败,我们就重新进入循环,读取最新的index值再尝试,直到成功为止。
  • 这里把index改成uint32是因为Go标准库的原子操作对这类基本数值类型支持更直接,能避免不必要的类型转换问题。

额外小提示

这种CAS循环是无锁编程里的常用操作,低竞争场景下比锁的性能更好,因为没有上下文切换的开销;但如果是超高竞争的场景,可能会出现较多的循环重试,这时候锁的表现可能更稳定——不过你的需求用这种方式完全能完美满足。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:16:57