Go语言 并发编程必要的原子操作 | Konica 的自留地

Go语言 并发编程必要的原子操作

Kitty, Cat, Kid Cat, Domestic Cat, Kitten, Gray, Animal

引言

举一个例子, 将变量 n 从 0 开始, 自增 1000 次, 然而对于并发操作而言, 当两个或两个以上的 goroutines 同时读取内存中的 n 值, 然后将 n+1 的值放回内存,可能多次自增的结果,这个 n 只自增了 1 次!

这里需要使用原子操作, 以保证多个 goroutines 对同一块内存的操作是原子的, 即保证本例中 n 自增到最后的值为 1000.

示例

本例中使用 Golang 标准库的 sync/atomic, 以实现原子操作;
本例中使用 nA, nB 变量来区分 n 值, 并对比最后的结果.

Go语言代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var (
wg sync.WaitGroup
nA, nB int64
)
wg.Add(2000)
for i := 0; i < 1000; i++ {
go func() {
nA++
wg.Done()
}()
}
for i := 0; i < 1000; i++ {
go func() {
atomic.AddInt64(&nB, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println(nA, nB)
}

运行结果 (多次实验):

Golang-atomic
可以看到, 并发操作中, nA 的值不太可能自增到 1000, nB 使用了原子操作自增, 保证了每次自增都能成功.