Mutexes
#
// 前の例で、単純なカウンタの状態を[アトミックな操作](atomic-counters.html)で管理する方法を紹介した。
// より複雑な状態を管理したいときには、[<em>ミューテックス(mutex)</em>](http://en.wikipedia.org/wiki/Mutual_exclusion)を使って複数のゴルーチンから安全にデータを読み書きすることもできる。
package main
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
func main() {
// 状態の例としてマップを使う。
var state = make(map[int]int)
// この `mutex` を使って、`state` へのアクセスを同期する。
var mutex = &sync.Mutex{}
// 読み操作・書き操作がそれぞれ何度行われたかをカウントする。
var readOps uint64
var writeOps uint64
// 100 個のゴルーチンを作り、それぞれが1ミリ秒ごとに state を繰り返し読み出す。
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
// 読み出しのたびに、アクセスするキーを選ぶ。
// `Lock()` で `mutex` をロックし、`state` への排他的なアクセスを獲得し、値を読み出し、`Unlock()` で `mutex` のロックを解除し、最後に `readOps` の値を1増やす。
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddUint64(&readOps, 1)
// 読み出しのたびに少し待つ。
time.Sleep(time.Millisecond)
}
}()
}
// 10個のゴルーチンを起動し、読み出しと同様のパターンで書き込みを行う。
for w := 0; w < 10; w++ {
go func() {
for {
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
// 10個のゴルーチンが `state` や `mutex` で仕事をできるよう、一秒待つ。
time.Sleep(time.Second)
// 結果を読み出して、読み書き操作のカウントを報告する
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
// `state` をロックして、最終的にどうなったかを表示する。
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
# プログラムを実行した結果、`mutex` で同期した操作を合計で約90000回実行したことがわかる。
$ go run mutexes.go
readOps: 83285
writeOps: 8320
state: map[1:97 4:53 0:33 2:15 3:2]
# 続いて、同様の状態管理をゴルーチンとチャネルだけを使って実装する方法を紹介する。