atomic.addint64是高并发累加首选,单指令原子操作且具内存序保障;其他方式如手动load+store或非原子++均不安全,且atomic变量必须地址稳定、类型匹配、全程统一访问。

atomic.AddInt64 是计数器的默认选择,不是“可选”
高并发下做累加,atomic.AddInt64 应该是第一直觉,而不是 atomic.LoadInt64 + atomic.StoreInt64 手动组合。前者是单条 CPU 原子指令(如 x86 的 LOCK XADD),天然带 acquire/release 内存语义;后者只是两步独立原子操作,中间可能被其他 goroutine 插入,且不保证顺序可见性。
- 用
counter++或counter += 1—— 必现竞态,结果不可靠 - 用
atomic.LoadInt64+atomic.StoreInt64模拟加法 —— 不是原子的,可能丢更新 - 用
atomic.AddInt64(&counter, 1)—— 推荐,简洁、安全、有内存序保障 - 类型必须是
int64(不是int),避免 32 位系统因未对齐 panic
CompareAndSwapInt32 失败不报错,是常态不是 bug
atomic.CompareAndSwapInt32(&x, old, new) 返回 false,只说明“此刻 x 的值不是 old”,不代表出错。这是无锁编程的基础前提——你得自己决定是否重试、何时放弃。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 典型误用:调一次就不管返回值,以为“没成功就算了”
- 正确模式:用 for 循环自旋,读 → 算 → CAS → 不成再读,例如实现“只在小于阈值时递增”
- 高竞争下别空转:加
runtime.Gosched()让出时间片,或time.Sleep(1ns)缓解 CPU 占用 - 状态标志(如启动/关闭)适合用 CAS,但别指望它自动阻塞或排队
结构体、map、float64 不能直接 atomic 操作
sync/atomic 只支持基础类型:int32、int64、uint32、uint64、uintptr、*unsafe.Pointer。其他一切类型都得绕道。
-
bool:必须用int32模拟(0/1),再配atomic.LoadInt32/atomic.StoreInt32 -
float32/float64:需先用math.Float64bits转成uint64,再用atomic.LoadUint64,注意 IEEE 754 表示和字节序 - 结构体 / map / slice:不能原子更新字段或元素,应改用
atomic.Value整体替换(它内部用读写锁+指针交换) - 千万别对结构体字段取地址后传给 atomic 函数——字段可能未对齐,尤其在 32 位 ARM 上直接 panic
所有 atomic 变量必须有稳定地址,且不能混用非 atomic 访问
一旦一个变量参与了 atomic 操作,它就必须始终通过 atomic 函数访问,且地址生命周期要足够长。栈上局部变量的地址不可靠,逃逸分析也救不了。
- ✅ 推荐:声明为包级变量,或用
new(int64)分配在堆上 - ❌ 危险:在函数里
var x int64; atomic.AddInt64(&x, 1)——x返回后地址失效,行为未定义 - ❌ 绝对禁止:对同一变量,一部分代码用
counter++,另一部分用atomic.AddInt64—— 破坏内存可见性和顺序保证 -
atomic.Value存指针时,要确保对象不被 GC 回收:要么是全局变量,要么用sync.Pool管理,不能只靠StorePointer维持引用
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










