golangの閉パケット内参照値とrange実装


問題の説明


簡単な遍歴機能
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)