簡體   English   中英

如何使用 goroutine 執行 for 循環?

[英]How can I use goroutine to execute for loop?

假設我有一個像這樣的切片:

stu = [{"id":"001","name":"A"} {"id":"002", "name":"B"}]可能還有更多這樣的元素。 切片內部是一個長字符串,我想使用 json.unmarshal 來解析它。

type Student struct {
   Id   string `json:"id"`
   Name string `json:"name"`
}

studentList := make([]Student,len(stu))
for i, st := range stu {
   go func(st string){
      studentList[i], err = getList(st)
      if err != nil {
         return ... //just example
      }
   }(st)
}
//and a function like this
func getList(stu string)(res Student, error){
   var student Student
   err := json.Unmarshal(([]byte)(stu), &student)
   if err != nil {
      return
   }
   return &student,nil
}

我得到了 nil 結果,所以我會說 goroutine 執行是亂序的,所以我不知道它是否可以使用 studentList[i] 來獲取值。

以下是您的代碼的一些潛在問題:

i的值可能不是你所期望的

for i, st := range stu {
   go func(st string){
      studentList[i], err = getList(st)
      if err != nil {
         return ... //just example
      }
   }(st)
}

你啟動了許多 goroutine,並在其中引用i 問題是i很可能在您啟動 goroutine 和 goroutine 引用它的時間之間發生了變化(for 循環與它啟動的 goroutine 同時運行)。 很可能for在任何 goroutine 之前完成意味着所有 output 將存儲在studentList的最后一個元素中(它們將相互覆蓋,因此您最終將得到一個值)。

一個簡單的解決方案是將i傳遞給 goroutine function (例如go func(st string, i int){}(st, i) (這將創建一個副本)。有關更多信息,請參閱此

Output 的學生名單

您沒有在問題中說,但我懷疑您在for循環完成后立即運行fmt.Println(studentList[1] (或類似的)。如上所述,很可能此時沒有一個 goroutines 完成(或者他們可能,你不知道)。使用WaitGroup是一種相當簡單的方法:

var wg sync.WaitGroup
wg.Add(len(stu))
for i, st := range stu {
    go func(st string, i int) {
        var err error
        studentList[i], err = getList(st)
        if err != nil {
            panic(err)
        }
        wg.Done()
    }(st, i)
}
wg.Wait()

我已經在操場上糾正了這些問題。

不是因為這個

goroutine 亂序執行

這里至少有兩個問題:

你不應該在 goroutine 中使用 for 循環變量i

多個 goroutine 讀取i , for 循環修改i ,這里是競爭條件。 為了使i按預期工作,請將代碼更改為:

for i, st := range stu {
   go func(i int, st string){
      studentList[i], err = getList(st)
      if err != nil {
         return ... //just example
      }
   }(i, st)
}

更重要的是,使用sync.WaitGroup等待所有 goroutine。

var wg sync.WaitGroup
for i, st := range stu {
   wg.Add(1)
   go func(i int, st string){
      defer wg.Done()

      studentList[i], err = getList(st)
      if err != nil {
         return ... //just example
      }
   }(i, st)
}

wg.Wait()

PS:(警告:可能並非總是如此)
這一行studentList[i], err = getList(st) ,雖然它可能不會導致數據競爭,但它對 cpu 緩存行不友好。 最好避免編寫這樣的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM