天天看點

golang 循環建立新協程,發現每次使用的循環變量都一樣,都是最後一個

問題描述

循環建立新協程,發現每次使用的循環變量都一樣,都是最後一個

package main

import (
  "fmt"
  "time"
)

func main() {

  type Student struct {
    Name string
    Age int
  }
  studentList := []*Student{
    {
      Name: "張三",
      Age: 13,
    },
    {
      Name: "李四",
      Age: 13,
    },
    {
      Name: "王五",
      Age: 13,
    },
  }
  for idx, stu := range studentList {
    go func() {
      fmt.Printf("%v: %v\n", idx, stu)
    }()
  }
  time.Sleep(3 * time.Second)
}      

輸出

2: &{王五 13}
2: &{王五 13}
2: &{王五 13}      

可以發現輸出的下标都是 2,對象都是王五,這是因為 idx 和 stu 每次循環時都是同一個變量,每次用新值覆寫舊值,子協程内部的 idx 和 stu 也都是跟主線程裡的idx 和 stu是同一個變量,如果主線程中idx和stu發生變化,子協程裡的兩個變量也會發生變化。循環速度太快,循環了三次才開始執行子協程代碼,導緻子協程執行時idx和 stu已經被覆寫成最新的了,是以三次列印出來的同樣的值。

解決方案:

在每次循環時重新定義變量,這樣每個協程使用的就是循環内部的變量,而不是外層循環時共享的變量

golang 循環建立新協程,發現每次使用的循環變量都一樣,都是最後一個

如果不好了解,可以改成下面這樣,上面的隻不過重新定義的兩個變量名字跟外層循環裡的變量名字一樣

for idx, stu := range studentList {
    index, student := idx, stu
    go func() {
      fmt.Printf("%v: %v\n", index, student)
    }()
  }      

輸出

1: &{李四 13}
0: &{張三 13}
2: &{王五 13}      

bye the way

下面這種企圖通過給子協程傳參的方式是無法解決上面的問題的,因為本質上傳給協程的都是同兩個變量,隻不過變量的值變化了三次,等開始執行協程代碼時,協程内部的周遊值已經被覆寫成最新修改的值了

golang 循環建立新協程,發現每次使用的循環變量都一樣,都是最後一個
2: &{王五 13}
2: &{王五 13}
2: &{王五 13}