👻
goroutineで気をつけること
for文で並行処理をしたい状況はたくさんあると思います。
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(i)
}()
}
wg.Wait()
上記のコードを実行すると、どのような結果が得られると思いますか?
5
10
10
10
7
10
10
10
10
10
実行するごとに、異なる結果が出ると思います。
これは、goroutineの中で変数i
を表示するときにはすでに、for
ループが終了してしまっているからです。
これを回避するには、ループのスコープの中で新しい変数を宣言します。
この変更により、各ゴルーチンはそれぞれのv
の値をキャプチャします。
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
v := i // 追加
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(v)
}()
}
wg.Wait()
これを実行すると、期待通りの結果が得られることがわかります。
0
2
3
1
7
9
8
4
6
5
また、以下のように無名関数の引数として渡すことでも、解決することができます。
nums := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var wg sync.WaitGroup
for _, v := range nums {
wg.Add(1)
go func(num int) {
defer wg.Done()
fmt.Println(num)
}(v)
}
wg.Wait()
Discussion