跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Go语言atomic包怎么用_Golang原子操作详解

atomic.LoadInt64读不到最新值是因为普通读写绕过内存屏障,必须与atomic.StoreInt64配对使用;atomic.AddInt64是硬件级原子指令,替代非原子的counter++;CAS失败是正常并发反馈,需循环重试;指针原子操作须严格类型一致;原子变量必须有稳定地址。 atomic.LoadInt64 读不到最新值?一定是读写没配对 直接用
*p
读一个被
atomic.StoreInt64
写过的变量,大概率读到旧值——这不是 bug,是绕过了内存屏障。Go 的 atomic 操作靠底层指令加内存序保证可见性,普通读写不参与这套 同步机制 。 写必须用
atomic.StoreInt64(&x, v)
,读就必须用
atomic.LoadInt64(&x)
,不能混用 哪怕
x
是包级变量、类型完全匹配,
fmt.Println(x)
也属于“普通读”,不可靠 ARM64 或低负载机器上更容易复现“卡在旧值”,因为缺少强制刷新缓存的语义 为什么 atomic.AddInt64(&counter, 1) 能替代 counter++?
counter++
在汇编层是“读 → 加1 → 写”三步,中间可能被其他 goroutine 插入;而
atomic.AddInt64
是单条 CPU 原子指令(如 x86 的
LOCK XADD
),硬件保证整个操作不可分割。 适用于高并发、低竞争场景:请求计数、指标打点、连接数统计 注意:
int64
在 32 位系统上不是自然对齐的,必须用
atomic
函数访问,否则 panic 或数据错乱 别传
*int
——
int
是平台相关类型,应显式用
int64
或
int32
,再取地址 CompareAndSwapInt64 不成功?不是失败,是并发控制的正常反馈
atomic.CompareAndSwapInt64(&x, old, new)
返回
false
,只说明“此刻
x
的值不是
old
”,不代表出错。这是无锁编程的设计前提,你得自己决定是否重试。 典型用法是 for 循环 + CAS 自旋,比如实现“只在小于阈值时递增” 高竞争下别空转:加
runtime.Gosched()
让出时间片,或短休眠(
time.Sleep(1ns)
)缓解 CPU 占用 别在循环里反复
var tmp int64 = atomic.LoadInt64(&x)
然后传地址——局部变量地址生命周期不够,行为未定义 指针原子操作:StorePointer / LoadPointer 必须严格类型一致
atomic.StorePointer
和
atomic.LoadPointer
操作的是
unsafe.Pointer
,不检查类型。存了
*Config
却当
*User
读,会直接崩溃或读出垃圾内存。 go语言参考手册 中文CHM版 Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译 下载 立即学习 “ go语言免费学习笔记(深入) ”; 存的时候:用
unsafe.Pointer(&cfg)
,其中
cfg
是具体类型的变量或指针 读的时候:必须用
(*Config)(atomic.LoadPointer(&ptr))
,括号里的类型要和存入时完全一致 跨函数传参时,别裸传
unsafe.Pointer
,容易丢失类型上下文;Go 1.19+ 可用泛型封装一层类型安全 wrapper 最常被忽略的一点:所有原子变量必须有稳定地址。包级变量 OK,
new(uint64)
分配的堆内存 OK,但切片元素、map value、结构体非导出字段、局部变量取地址——全都危险。不是编译不过,是运行时行为不可控。

相关文章