これは、「Race on counter loop」の典型的な例です。コードを実行すると、go run -race
それがわかると思います。
以下はあなたが期待することを行います:
func main() {
completed := make(chan bool, 2)
m := map[string]string{"a": "a", "b": "b"}
for k, v := range m {
go func(k, v string) {
fmt.Println(k, v)
completed <- true
}(k, v)
}
<- completed
<- completed
}
元のコードは、どのマシンでも b のみ (または a のみ) を出力する可能性が高く、実際には Go プレイグラウンドで発生します: http://play.golang.org/p/Orgn030Yfr
これは、ゴルーチンが作成された時点でこれらの変数がたまたま持っている値ではなく、無名関数が行の変数を参照しているためです。最初に両方の変数が 1 つの値に設定され、1 つのゴルーチンが生成されます。次に、それらが別の値に設定され、別のゴルーチンが生成されます。次に、両方の goroutine が実行され、両方とも k と v の最新の値が表示されます。ちなみに、これはマルチスレッドや Go に固有のものではありません (play.golang.org はすべてを単一のスレッドで実行し、この "これと同じ問題は、スレッドが 1 つだけであることが保証されている JavaScript でも発生します。for k, v
obj = {a: 'a', b: 'b'};
for (k in obj) {
setTimeout(function() { console.log(k, obj[k]); }, 0);
}
http://goo.gl/vwrMQ -- 匿名関数が実行されるまでに for ループが終了しているため、「k」には関数の両方の実行の最新の値が残されます。