以範例學習 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

接下來,我們將看一下互斥鎖,這是管理狀態的另一項工具。

下一個範例:互斥鎖