Go语言原子操作完全指南
# 前言
原子操作是指不可中断的一个或一系列操作,这些操作要么全部执行成功,要么全部不执行。在Go语言中,通过sync/atomic
包提供原子操作支持。允许在不适用互斥锁的情况下安全地并发访问共享数据,加锁会引入协调开销,性能可能会下降,而原子操作使用 CPU 指令直接在硬件层面进行操作,从而有更高的性能。
# 原子操作 vs 互斥锁
特性 | 原子操作 | 互斥锁 |
---|---|---|
性能 | 更高(CPU指令级支持) | 较低(需要系统调用) |
使用场景 | 简单内存操作 | 复杂代码块保护 |
可操作性 | 有限(仅支持基本类型) | 灵活(可保护任意代码) |
# 原子操作使用场景
# 1. 计数器实现
var requestCount atomic.Int64 // Go 1.19+新版API
// 处理请求时安全递增计数器
func HandleRequest() {
requestCount.Add(1) // 原子递增
}
// 获取当前请求数
func GetRequestCount() int64 {
return requestCount.Load()
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 2. 状态标志控制
var shutdown atomic.Int32
func mainLoop() {
for {
if shutdown.Load() == 1 {
break
}
// do work
}
}
func stop() {
shutdown.Store(1)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 单次初始化(替代sync.Once)
var initialized atomic.Int32
func maybeInit() {
if initialized.CompareAndSwap(0, 1) {
// 只有第一个调用者会执行这里
}
// 其他调用者直接返回
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4. 无锁栈数据结构实现
type node struct {
next *node
val any
}
var head atomic.Pointer[node]
func push(n *node) {
for {
old := head.Load()
n.next = old
if head.CompareAndSwap(old, n) {
return
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 性能对比测试
针对使用互斥锁和原子操作的 Benchmark
func BenchmarkAtomicIncrement(b *testing.B) {
var counter atomic.Int64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
counter.Add(1)
}
})
}
func BenchmarkMutexIncrement(b *testing.B) {
var (
counter int64
mu sync.Mutex
)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock()
counter++
mu.Unlock()
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
运行结果如下,原子操作要比加互斥锁快上约 40%。
$ go test -bench=. -benchmem .
goos: darwin
goarch: arm64
pkg: main/demo
cpu: Apple M4 Pro
BenchmarkAtomicIncrement-12 22237474 55.00 ns/op 0 B/op 0 allocs/op
BenchmarkMutexIncrement-12 13650686 84.53 ns/op 0 B/op 0 allocs/op
PASS
ok main/demo 2.841s
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 总结
优先使用原子操作的场景:
- 计数器、状态标志等简单共享变量
- 性能敏感的并发控制
- 无锁数据结构实现
需要使用互斥锁的场景:
- 保护复杂逻辑代码块
- 需要保护多个变量的不变
- 执行IO操作等耗时任务时
上次更新: 2025/06/14, 16:16:07