golangの閉パケット内参照値とrange実装
15817 ワード
問題の説明
簡単な遍歴機能
func demo0(){
// , i,a ,
arr := []string{"dog", "cat", "mouse"}
for i, a := range arr{
fmt.Printf("func %d get %s
", i, a)
}
}
出力:
func 0 get dog
func 1 get cat
func 2 get mouse
そしてgoroutineで同時実行を完了したい
func demo2(){
arr := []string{"dog", "cat", "mouse"}
wg := &sync.WaitGroup{}
for i, a := range arr{
wg.Add(1)
go func(){
fmt.Printf("func %d get %s
", i, a)
wg.Done()
}()
}
wg.Wait()
}
しかし、出力は次のようになります.
func 2 get mouse
func 2 get mouse
func 2 get mouse
配列の最後の値のみを取得
原因分析
ここではrangeの実装とgo閉パケットが外部値を参照する形式の2つの問題に関連する.
rangeの実現
func demo0(){
// , i,a ,
arr := []string{"dog", "cat", "mouse"}
for i, a := range arr{
fmt.Printf("func %d(%v) get %s(%v)
", i, &i, a, &a)
}
}
iとaのアドレスをプリントアウトすると、iとaのアドレスが変わっていないことがわかります.range操作は
var a string
for i := 0; i < len(arr); i++{
a = arr[i]
fmt.Printf("func %d(%v) get %s(%v)
", i, &i, a, &a)
}
go閉パケットが外部値を参照する形式
func()内部のiとaは、アドレス値で参照する、また実行時にアドレス値に基づいて値がどれだけあるかを見つけるため、arr配列の最後の値を指す.
次の例はもっと明らかです
func demo3(){
//
arr := []string{"dog", "cat", "mouse"}
funcArr := make([]func(), 0) //
for i, a := range arr{
goFunc := func(){
fmt.Printf("func %d(%v) get %s(%v)
", i, &i, a, &a)
}
funcArr = append(funcArr, goFunc)
}
for _, f := range funcArr{
f()
}
}
しゅつりょく
func 2(0xc00008c020) get mouse(0xc00008e030)
func 2(0xc00008c020) get mouse(0xc00008e030)
func 2(0xc00008c020) get mouse(0xc00008e030)
f()呼び出し時に閉パケット内で使用される外部値を決定する
解決策
解決策も簡単で、外部値を参照するのではなくパラメータを使用して値を伝達すればよい.
func demo2(){
arr := []string{"dog", "cat", "mouse"}
wg := &sync.WaitGroup{}
for i, a := range arr{
wg.Add(1)
go func(i int, a string){
fmt.Printf("func %d(%v) get %s(%v)
", i, &i, a, &a)
wg.Done()
}(i, a)
}
wg.Wait()
}
あるいはfor内部で新しい変数を使っても解決できます
func demo2(){
arr := []string{"dog", "cat", "mouse"}
wg := &sync.WaitGroup{}
for i, a := range arr{
wg.Add(1)
index, val := i, a //
go func(){
fmt.Printf("func %d(%v) get %s(%v)
", index, &index, val, &val)
wg.Done()
}()
}
wg.Wait()
}
しゅつりょく
func 2(0xc00001c0e0) get mouse(0xc000010240)
func 1(0xc00001c0f0) get cat(0xc000010260)
func 0(0xc00008c000) get dog(0xc00008e000)