Atomic Counters
#
// Go で状態を管理するとき、まず検討すべきはチャネル通信を使う方法である。
// このような例は既に[ワーカープール](./worker-pools.html)で紹介した。
//
// 別のやり方もある。
// ここでは、`sync/atomic` パッケージにある、複数のゴルーチンからアクセスされる<em>アトミックなカウンタ</em>を使う方法を紹介する。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
// 符号無し整数型の変数で(負の値を取らない)カウンタを表現する
var ops uint64
// WaitGroup を使ってすべてのゴルーチンが仕事を終えるのを待つ
var wg sync.WaitGroup
// 更新を並行に実行するため、50個のゴルーチンを起動する。
// 各ゴルーチンは約1ミリ秒ごとにカウンタの値を1増やす。
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)
}
# ちょうど50000回操作を実行するはずだ。
# このときにアトミックではない `ops++` を使うと、ゴルーチンが相互に干渉しあうので、
# カウンタの値は実行のたびに異なる(そして50000でもない)値になるだろう。
# `-race` フラグを使ってデータアクセスの競合を検出することもできる。
$ go run atomic-counters.go
ops: 50000
# 次の例では、状態管理のためのもう一つのツールである、
# ミューテックスを紹介する。