前の例で、単純なカウンタの状態をアトミックな操作で管理する方法を紹介した。 より複雑な状態を管理したいときには、ミューテックス(mutex)を使って複数のゴルーチンから安全にデータを読み書きすることもできる。 |
|
![]() ![]() package main
|
|
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
)
|
|
func main() {
|
|
状態の例としてマップを使う。 |
var state = make(map[int]int)
|
この |
var mutex = &sync.Mutex{}
|
読み操作・書き操作がそれぞれ何度行われたかをカウントする。 |
var readOps uint64
var writeOps uint64
|
100 個のゴルーチンを作り、それぞれが1ミリ秒ごとに state を繰り返し読み出す。 |
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
|
読み出しのたびに、アクセスするキーを選ぶ。
|
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個のゴルーチンが |
time.Sleep(time.Second)
|
結果を読み出して、読み書き操作のカウントを報告する |
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
|
|
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
|
プログラムを実行した結果、 |
$ go run mutexes.go
readOps: 83285
writeOps: 8320
state: map[1:97 4:53 0:33 2:15 3:2]
|
続いて、同様の状態管理をゴルーチンとチャネルだけを使って実装する方法を紹介する。 |