3
package main

import "fmt"

func main() {
  completed := make(chan bool, 2)
  m := map[string]string{"a": "a", "b": "b"}
  for k, v := range m {
    go func() {
      fmt.Println(k, v)
      completed <- true
    }()
  }
  <- completed
  <- completed
}

コードを何百回も実行しましたが、出力は常に次のようになります。

b b
b b

a aしかし、ペアプリントは見たことがありません。これはある種の奇妙な並行性の問題ですか?

4

3 に答える 3

6

これは、「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」には関数の両方の実行の最新の値が残されます。

于 2013-06-14T04:39:16.063 に答える
0

仕様によると、「マップの反復順序は指定されておらず、反復ごとに同じであることが保証されていません」。

基本的に、反復が特定の順序で行われることを期待すべきではありません。ご覧のとおり、現在の の実装ではrange常にこの出力が生成される可能性がありますが、状況によって、または Go の次のバージョンでは異なる可能性があります。

マップ キーを特定の順序で反復処理する場合は、次のようにスライスを使用して自分で指定できます。

http://blog.golang.org/go-maps-in-action

于 2013-06-14T03:08:44.543 に答える