Go 中 sync/atomic 通过底层 CPU 原子指令实现无锁高性能计数,比 mutex 更轻量高效;仅支持基础类型原子操作,需取地址调用且避免与普通赋值混用。

在 Go 中,sync/atomic 是实现无锁、高性能并发计数的核心工具。它直接映射到底层 CPU 的原子指令(如 LOCK XADD),比加锁更轻量,适合高频读写计数器场景。
为什么用 atomic 而不是 mutex?
使用 sync.Mutex 保护一个整型计数器虽然安全,但每次增减都要获取/释放锁,存在调度开销和竞争排队;而 atomic 操作是单条 CPU 指令,无上下文切换,吞吐更高。尤其在多核高并发下,性能差距明显。
注意:atomic 只支持基础类型(int32, int64, uint32, uint64, uintptr, *unsafe.Pointer)及其指针操作,不能用于结构体或 map 等复合类型。
基础计数器:增减与读取
声明计数器时必须用支持原子操作的类型(推荐 int64,避免 32 位平台对齐问题),并确保变量地址对齐(全局变量或堆分配自动满足):
立即学习“go语言免费学习笔记(深入)”;
✅ 正确示例:
- 定义:
var counter int64(全局或包级变量) - 原子自增:
atomic.AddInt64(&counter, 1) - 原子读取:
atomic.LoadInt64(&counter) - 原子写入:
atomic.StoreInt64(&counter, 100) - 条件更新(CAS):
atomic.CompareAndSwapInt64(&counter, old, new),仅当当前值等于old时才设为new,返回是否成功
常见陷阱与规避方式
— 计数器必须取地址传递给 atomic 函数,传值会报编译错误;
— 不要混用 atomic 和普通赋值(如 counter = 42),这会破坏原子性;
— 避免在非对齐内存上使用(如 struct 中未导出的 int32 字段紧邻其他字段),可能导致 panic(尤其在 ARM 上);
— atomic.Value 用于安全读写任意类型(如 map[string]int),但它不提供原子增减,只做整体替换。
完整可运行示例
以下是一个启动 10 个 goroutine 并发累加 1000 次的计数器:
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
var counter int64
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
atomic.AddInt64(&counter, 1)
}
}()
}
wg.Wait()
fmt.Println("Final count:", atomic.LoadInt64(&counter)) // 输出 10000
}
若换成普通 counter++ 且无锁,结果大概率小于 10000 —— 这正是竞态的典型表现。










