Goroutine の動作: 謎を解く
提供された Go コードで、次のような複雑な動作に遭遇します。
package main import ( "fmt" "time" ) type field struct { name string } func (p *field) print() { fmt.Println(p.name) } func main() { data := []field{{"one"}, {"two"}, {"three"}} for _, v := range data { go v.print() } <-time.After(1 * time.Second) }
疑問が生じます:なぜこのコードは常に「three」を表示せずに 3 回出力するのでしょうか。 「1 つ」、「2 つ」、「3 つ」は順不同ですか?
問題の理解
問題の核心は、次のような原因で引き起こされる微妙な競合状態にあります。 goroutine 関数での範囲変数 v の使用。
v.print() を記述するとき、事実上変数 v へのポインタを渡していることになります。これは、範囲データ ループ内の現在の要素への参照です。ただし、ループは反復を続け、v の値を変更します。
ゴルーチンが実行されると、v の最終値は "three" になります。これにより、3 つの「three」という予期しない出力が生成されます。
問題の解決: 複数のアプローチ
この問題を解決するには、いくつかの方法があります:
1.短い変数宣言の使用:
ループの各反復をスコープとする新しい変数 v を作成します:
for _, v := range data { v := v // Short variable declaration to create a new `v`. go v.print() }
2.ポインタのスライスの使用:
データのタイプをポインタのスライスに変更し、個々のポインタを goroutine 関数に渡します:
data := []*field{{"one"}, {"two"}, {"three"}} // Note the '*' for _, v := range data { go v.print() }
3.スライス要素のアドレスを使用する:
各スライス要素のアドレスを取得し、ポインタを goroutine 関数に渡します:
data := []*field{{"one"}, {"two"}, {"three"}} // Note the '*' for i := range data { v := &data[i] go v.print() }
結論
ループが範囲変数の値を変更する場合、範囲変数のアドレスを取得すると、ゴルーチンで予期しない動作が発生する可能性があることに注意してください。変数。上記で概説した手法を使用すると、各ゴルーチンが確実に一意の値を受け取り、データ競合の問題を回避できます。
以上がこの Go コードでは、「One」、「Two」、「Three」ではなく「Three」が 3 回表示されるのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。