Go による実例: アトミックカウンター

Go で状態を管理するための主なメカニズムは、チャネルを介した通信です。たとえば、ワーカープールでその内容が確認できます。ただし、状態を管理するための他のオプションもいくつかあります。ここでは、複数のゴルーチンからアクセスされる_アトミックカウンター_に sync/atomic パッケージを使用する方法について説明します。

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

アトミック整数型を使用して、(常に正の)カウンターを表します。

    var ops atomic.Uint64

WaitGroup を使用して、すべてのゴルーチンが作業を完了するのを待ちます。

    var wg sync.WaitGroup

カウンターを正確に 1000 回ずつ増加させる、50 個のゴルーチンを開始します。

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

カウンターをアトミックに増加させるには、Add を使用します。

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

すべてのゴルーチンが完了するまで待ちます。

    wg.Wait()

ここでは、どのゴルーチンも「ops」に書き込みを行っていませんが、Load を使用すると、他のゴルーチンが(アトミックに)それを更新している場合でも、値をアトミックに読み取ることが安全です。

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

正確に 50,000 の操作が得られることを期待します。非アトミック整数を使用して ops++ で増加した場合、ゴルーチンが相互に干渉するため、実行ごとに変化する異なる数値が得られる可能性があります。さらに、-race フラグを付けて実行するとデータ競合の障害が発生します。

$ go run atomic-counters.go
ops: 50000

次は、状態を管理するための別のツールであるミューテックスについて説明します。

次の例: ミューテックス