在线文档 > Golang练习 > 原子计数器
在 Go 中,管理状态的主要机制是通过通道进行通信。我们在工作池中看到过这个例子。
然而,还有其他一些选项来管理状态。这里,我们将看看如何使用sync/atomic
包处理多个协程访问的原子计数器。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
// 我们将使用无符号整数来表示我们的(始终为正的)计数器。
var ops uint64
// WaitGroup帮助我们等待所有协程完成它们的工作。
var wg sync.WaitGroup
// 我们将启动50个协程,每个协程精确地增加计数器到1000次。
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
for c := 0; c < 1000; c++ {
// 要原子性地增加计数器,我们使用`AddUint64`,并使用“&”语法获取我们的“ops”计数器的内存地址。
atomic.AddUint64(&ops, 1)
}
wg.Done()
}()
}
// 等待直到所有的协程完成。
wg.Wait()
// 由于我们知道没有其他 协程 正在写入它,因此现在可以安全地访问`ops`。像使用`atomic.LoadUint64`这样的函数,在原子更新的同时安全地读取它们。
fmt.Println("ops:", ops)
}
运行结果如下:
$ go run atomic-counters.go
ops: 50000
我们预计正好得到 50000 次操作。如果我们使用非原子的ops++
来增加计数器,由于多个协程会互相干扰,运行时值会改变,可能会导致我们得到一个不同的数字。 此外,运行程序时带上 -race
标志,我们可以获取数据竞争失败的详情。
接下来,我们看一下管理状态的另一个工具——互斥锁。