前回の例では、アトミックな操作 を使って、 シンプルなカウンターの状態を管理する方法を見ました。 より複雑な状態の場合は、複数のゴルーチンから安全にデータへ アクセスするために ミューテックス (mutex) を使えます。 |
|
package main |
|
import ( "fmt" "sync" ) |
|
Container は、カウンタのマップを保持します。
複数のゴルーチンから並行して更新したいので、
アクセスを同期化するために |
type Container struct { mu sync.Mutex counters map[string]int } |
|
func (c *Container) inc(name string) { |
c.mu.Lock() defer c.mu.Unlock() c.counters[name]++ } |
|
ミューテックスはゼロ値がそのまま使えるので、 ここでは初期化が不要である点に注意してください。 |
func main() { c := Container{ |
counters: map[string]int{"a": 0, "b": 0}, } |
|
var wg sync.WaitGroup |
|
この関数はループを使って名前付きのカウンタをインクリメントします。 |
doIncrement := func(name string, n int) { for i := 0; i < n; i++ { c.inc(name) } wg.Done() } |
複数のゴルーチンを並行実行します。これらはすべて同じ
|
wg.Add(3) go doIncrement("a", 10000) go doIncrement("a", 10000) go doIncrement("b", 10000) |
ゴルーチンが完了するのを待ちます。 |
wg.Wait() fmt.Println(c.counters) } |
プログラムを実行すると、カウンタが期待通りに 更新されたことが分かります。 |
$ go run mutexes.go map[a:20000 b:10000] |
次回は、これと同じ状態管理をゴルーチンとチャネルだけを使って 実装する例を見ていきましょう。 |
Next example: Stateful Goroutines.