# 前言

原子操作是指不可中断的一个或一系列操作,这些操作要么全部执行成功,要么全部不执行。在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. 状态标志控制

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

# 3. 单次初始化(替代sync.Once)

var initialized atomic.Int32

func maybeInit() {
    if initialized.CompareAndSwap(0, 1) {
        // 只有第一个调用者会执行这里
    }

    // 其他调用者直接返回
}

1
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

# 性能对比测试

针对使用互斥锁和原子操作的 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

运行结果如下,原子操作要比加互斥锁快上约 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

# 总结

  • 优先使用原子操作的场景:

    • 计数器、状态标志等简单共享变量
    • 性能敏感的并发控制
    • 无锁数据结构实现
  • 需要使用互斥锁的场景:

    • 保护复杂逻辑代码块
    • 需要保护多个变量的不变
    • 执行IO操作等耗时任务时