Go同步原语与数据竞争:原子操作(atomic)

在Go并发编程中,当多个 goroutine 同时读写共享变量时,如果没有妥善同步,就会出现数据竞争(Data Race) 。Go 提供了 sync/atomic 包,用于实现轻量级的原子操作,避免使用锁所带来的性能开销。


一、什么是原子操作?

原子操作指的是在执行过程中不会被任何其他操作中断的操作。在多线程环境中,原子操作确保某个变量的读、写、加减等操作具有一致性和安全性。


二、常用原子操作函数

Go 的 sync/atomic 包支持以下常用操作,主要用于 int32、int64、uint32、uint64 和 unsafe.Pointer 等类型:

函数 说明
atomic.LoadInt32(&val) 原子读取值
atomic.StoreInt32(&val, new) 原子写入值
atomic.AddInt32(&val, delta) 原子加法(并返回新值)
atomic.CompareAndSwapInt32(&val, old, new) 原子比较并交换
同理还有 Int64、Uint32、Uint64、Pointer 版本

三、使用示例

示例:并发计数器

go 复制代码
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var counter int32 = 0
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            atomic.AddInt32(&counter, 1)
        }()
    }

    wg.Wait()
    fmt.Println("Final Counter:", counter) // 输出应该为 1000
}

这个例子中我们使用 atomic.AddInt32 来确保并发写入是安全的,避免了 race condition。


四、CompareAndSwap:原子级条件更新

CompareAndSwap 是一个非常强大的函数,可用于实现无锁状态切换。

go 复制代码
var status int32 = 0

// 尝试将状态从 0 改为 1
if atomic.CompareAndSwapInt32(&status, 0, 1) {
    fmt.Println("切换状态成功")
} else {
    fmt.Println("状态已被修改")
}

如果当前值等于期望值,就会被新值替换;否则不做任何操作,适用于状态机、CAS 重试等场景。


五、原子 vs 锁

方面 原子操作(atomic) 锁(Mutex/RWMutex)
性能 极高(CPU级指令) 较低(涉及调度、抢占)
适用场景 简单计数、状态标记 复杂结构同步
编程复杂度 较高,易出错 较低,语义清晰
死锁风险 无 存在风险

六、使用注意事项

  • • 原子操作仅适用于简单变量的并发读写,如计数器、标志位。
  • • 不能对结构体、map、slice 等复杂类型直接使用。
  • • 原子操作不能和普通操作混用,否则仍可能产生竞态条件。
  • • 如需读写多个变量或复合结构,推荐使用 sync.Mutex。

七、小结

  • • sync/atomic 提供了高性能的原子操作,是无锁并发的核心工具。
  • • 适合用于计数器、自旋锁、状态标识等场景。
  • • 不适合管理复杂共享数据,不能代替所有同步手段。

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
晨米酱4 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
大勇前进4 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu4 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile4 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白804 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙4 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端