Go 示例: 原子计数器

Go 中管理状态的主要机制是通过通道进行通信。例如,我们在 工作池 中看到了这一点。不过,还有其他几种管理状态的方法。在这里,我们将看看如何使用 sync/atomic 包来处理由多个 goroutine 访问的原子计数器

package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
func main() {

我们将使用原子整数类型来表示我们的(始终为正的)计数器。

    var ops atomic.Uint64

WaitGroup 将帮助我们等待所有 goroutine 完成其工作。

    var wg sync.WaitGroup

我们将启动 50 个 goroutine,每个 goroutine 将计数器递增 1000 次。

    for i := 0; i < 50; i++ {
        wg.Add(1)
        go func() {
            for c := 0; c < 1000; c++ {

为了原子地递增计数器,我们使用 Add

                ops.Add(1)
            }
            wg.Done()
        }()
    }

等待所有 goroutine 完成。

    wg.Wait()

这里没有 goroutine 写入 'ops',但使用 Load,即使其他 goroutine 正在(原子地)更新它,也可以安全地原子地读取值。

    fmt.Println("ops:", ops.Load())
}

我们预计将获得正好 50,000 次操作。如果我们使用非原子整数并使用 ops++ 递增它,我们可能会得到不同的数字,并在运行之间发生变化,因为 goroutine 会相互干扰。此外,在使用 -race 标志运行时,我们会得到数据竞争错误。

$ go run atomic-counters.go
ops: 50000

接下来,我们将看看互斥锁,这是另一种管理状态的工具。

下一个示例:互斥锁.