2017-11-24 31 views
1

関数のスライスをループし、その中のすべての関数を呼び出しようとしています。しかし、私は奇妙な結果を得ています。私のコードは次のとおりです:各関数を繰り返し関数スライスで呼び出す

私はそれが関数A、B、そしてCを呼び出すと思っていましたが、私の出力はCしか得られません。

何が間違っているのか、その背後にある論理を示唆してください。また、どのように私は希望の動作を得ることができます。

Go Playground

答えて

4

クラシック行く落とし穴:)

公式ゴーFAQ

for a, _ := range f { 
    wg.Add(1) 
    a:=a // this will make it work 
    go func() { 
     defer wg.Done() 
     f[a]() 
    }() 
} 

あなたfunc() {}()aの上に閉じ閉鎖です。 aは、すべてのgo func goルーチンで共有されます。forループは同じvar(同じ値をメモリ内に意味し、同じ値を意味する)を再利用するため、当然彼らはすべて最後の値aを参照します。

解決策は、上記のように閉じる前にa:=aを再宣言してください。これにより、新しいvar(メモリ内の新しいアドレス)が作成され、それはgo funcの呼び出しごとに新しいものになります。

それとも、その場合には、あなたがそうのようaの値のコピーを渡し、外出先での関数のパラメータとして渡す:

go func(i int) { 
    defer wg.Done() 
    f[i]() 
}(a) 

あなたも、このhttps://play.golang.org/p/nkP9YfeOWF例えば、外出先ルーチンを持っている必要はありません。同じようなことを示しています。ここの鍵は「閉鎖」です。

1

問題は、goroutineに希望の値を渡していないことが原因で、変数値が外側スコープから取得されているようです。つまり、最初のgoroutineが実行される前でも範囲の反復処理が完了しているため、常にインデックスa == 2を取得しています。つまり、関数Cです。 time.Sleep(100)をinsideあなたの範囲、ちょうどゴルーチンは、次の繰り返しに進む前に、メインスレッドに追いつくことを可能にする - >GO playground

for a, _ := range f { 
    wg.Add(1) 
    go func() { 
     defer wg.Done() 
     f[a]() 
    }() 
    time.Sleep(100) 
} 

出力

A 
B 
C 

何がやりたいことは、単に合格ですが、関数のコピーを作るゴルーチンへのパラメータ。

func main() { 
    type fs func() 
    var wg sync.WaitGroup 
    f := []fs{A, B, C} 
    for _, v := range f { 
     wg.Add(1) 
     go func(f fs) { 
      defer wg.Done() 
      f() 
     }(v) 
    } 
    wg.Wait() 
} 

GO Playground

関連する問題