Go语言中必须用atomic.AddInt64对共享int64变量原子递增,因i++非原子易致竞态,mutex性能差,而atomic底层用CPU原子指令高效安全;需确保变量8字节对齐、传指针、配合atomic.LoadInt64读取。

Go 语言里不能直接用 ++ 或 += 对共享变量做原子递增,必须用 atomic.AddInt64 —— 它是唯一安全、无锁、跨 goroutine 可靠的整数递增方式。
为什么不能用普通加法操作
多个 goroutine 同时执行 i++ 会导致竞态:读取、加 1、写回三步不是原子的,中间可能被其他 goroutine 打断。即使加了 sync.Mutex,性能开销大,而 atomic.AddInt64 底层用 CPU 原子指令(如 x86 的 XADD),快且轻量。
- 运行
go run -race main.go会明确报出Data Race错误 -
atomic.AddInt64要求参数地址必须对齐(int64在 64 位系统上需 8 字节对齐),结构体字段顺序或填充不当会触发 panic - 它只接受
*int64,不能传int、int32或局部变量地址(如&i,其中i是函数内定义的int64变量——这没问题;但若i是int就必须先转成int64再取地址)
atomic.AddInt64 的典型写法和常见错误
正确写法是声明一个全局或包级 int64 变量,用 & 取其地址传入;返回值是新值(不是旧值,注意和 atomic.LoadInt64 区分)。
var counter int64
func increment() {
newVal := atomic.AddInt64(&counter, 1) // ✅ 正确:+1,返回递增后的值
fmt.Println(newVal)
}
// ❌ 错误示例:
// atomic.AddInt64(&counter, 1.5) // 编译失败:第二个参数必须是 int64
// atomic.AddInt64(counter, 1) // 编译失败:第一个参数必须是指针
// atomic.AddInt64(&someInt32, 1) // 编译失败:类型不匹配
和 sync/atomic 其他函数的配合使用场景
atomic.AddInt64 通常和 atomic.LoadInt64、atomic.StoreInt64 搭配,构成完整的原子读-改-写闭环;不建议混用 sync.Mutex 和 atomic 操作同一变量。
立即学习“go语言免费学习笔记(深入)”;
- 计数器场景(如请求总数、失败次数):只用
AddInt64+LoadInt64 - 带条件更新(如仅当当前值为 0 时设为 1):要用
atomic.CompareAndSwapInt64,而不是靠AddInt64 - 初始化后只读或只增:无需额外同步,
atomic.AddInt64自身已保证内存可见性(happens-before 语义) - 如果变量嵌在结构体里,确保它是首字段或前面有足够 padding,否则可能因不对齐 panic(尤其在 ARM 平台更敏感)
性能与可移植性提醒
atomic.AddInt64 在 amd64 上极快,但在 32 位平台(如 386 或某些嵌入式 ARM)上,int64 原子操作可能被降级为锁实现,性能下降明显;若需兼容 32 位,优先考虑 atomic.AddInt32 + 显式范围控制,或改用 sync/atomic 提供的 Uint64 类型配合 atomic.LoadUint64/atomic.AddUint64(行为一致,语义更清晰)。
真正容易被忽略的是:哪怕你只在一个 goroutine 里调用 atomic.AddInt64,只要该变量可能被其他 goroutine 读,就必须用 atomic.LoadInt64 读取——普通读取无法保证看到最新写入值。


















