溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Golang中如何解決for-loop與goroutine的問題

發(fā)布時(shí)間:2021-08-07 11:24:36 來源:億速云 閱讀:116 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關(guān)Golang中如何解決for-loop與goroutine的問題的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

參見如下代碼:

for i := 0; i < len(rf.peers); i++ {
  DPrintf("i = %d", i)

  if i == rf.me {
   DPrintf("skipping myself #%d", rf.me)
   continue
  }

  go func() {
   DPrintf("len of rf.peers = %d", len(rf.peers))
   DPrintf("server #%d sending request vote to server %d", rf.me, i)
   reply := &RequestVoteReply{}
   ok := rf.sendRequestVote(i, args, reply)
   if ok && reply.VoteGranted && reply.Term == rf.currentTerm {
    rf.voteCount++
    if rf.voteCount > len(rf.peers)/2 {
     rf.winElectionCh <- true
    }
   }
  }()
}

其中,peers切片的長度為3,因此最高下標(biāo)為2,在非并行編程中代碼中的for-loop應(yīng)該是很直觀的,我當(dāng)時(shí)并沒有意識(shí)到有什么問題??墒窃谡{(diào)試過程中,一直在報(bào) index out of bounds 錯(cuò)誤。調(diào)試信息顯示i的值為3,當(dāng)時(shí)就一直想不明白循環(huán)條件明明是 i < 2,怎么會(huì)變成3呢。

分析

雖然不明白發(fā)生了什么,但知道應(yīng)該是循環(huán)中引入的 goroutine 導(dǎo)致的。經(jīng)過Google,發(fā)現(xiàn)Go的wiki中就有一個(gè)頁面 Common Mistake - Using goroutines on loop iterator variables 專門提到了這個(gè)問題,看來真的是很 common 啊,笑哭~

初學(xué)者經(jīng)常會(huì)使用如下代碼來并行處理數(shù)據(jù):

for val := range values {
 go val.MyMethod()
}

或者使用閉包(closure):

for val := range values {
 go func() {
  fmt.Println(val)
 }()
}

這里的問題在于 val 實(shí)際上是一個(gè)遍歷了切片中所有數(shù)據(jù)的單一變量。由于閉包只是綁定到這個(gè) val 變量上,因此極有可能上面的代碼的運(yùn)行結(jié)果是所有 goroutine 都輸出了切片的最后一個(gè)元素。這是因?yàn)楹苡锌赡墚?dāng) for-loop 執(zhí)行完之后 goroutine 才開始執(zhí)行,這個(gè)時(shí)候 val 的值指向切片中最后一個(gè)元素。

The val variable in the above loops is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.

解決方法

以上代碼正確的寫法為:

for val := range values {
 go func(val interface{}) {
  fmt.Println(val)
 }(val)
}

在這里將 val 作為一個(gè)參數(shù)傳入 goroutine 中,每個(gè) val 都會(huì)被獨(dú)立計(jì)算并保存到 goroutine 的棧中,從而得到預(yù)期的結(jié)果。

另一種方法是在循環(huán)內(nèi)定義新的變量,由于在循環(huán)內(nèi)定義的變量在循環(huán)遍歷的過程中是不共享的,因此也可以達(dá)到同樣的效果:

for i := range valslice {
 val := valslice[i]
 go func() {
  fmt.Println(val)
 }()
}

對(duì)于文章開頭提到的那個(gè)問題,最簡單的解決方案就是在循環(huán)內(nèi)加一個(gè)臨時(shí)變量,并將后面 goroutine 內(nèi)的 i 都替換為這個(gè)臨時(shí)變量即可:

server := i

感謝各位的閱讀!關(guān)于“Golang中如何解決for-loop與goroutine的問題”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI