Fix for-range Issue Again in Go 1.22

  sonic0002        2024-11-21 01:03:04       1,317        1          English  简体中文  繁体中文  ภาษาไทย  Tiếng Việt 

隨著 Go 1.22 的發布,Go 團隊解決了長期存在的問題,這個問題與for-range迴圈變數的作用域有關,一直以來都是撰寫併發程式碼的開發人員常見的陷阱。具體來說,問題在於for-range迴圈中的迴圈變數在每次迭代中都被重複使用,當涉及閉包時,就會導致意想不到的行為。

Go 1.22 之前的問題

考慮以下範例:

package forange

import "fmt"

func Do() {
    done := make(chan bool)
    values := []string{"a", "b", "c"}

    for _, v := range values {
        go func() {
            fmt.Println(v) // 輸出非預期值
            done <- true
        }()
    }

    for _ = range values {
        <-done
    }
}

並且有一個go.mod檔案如下

module playground

go 1.16

require github.com/ozgio/strutil v0.4.0

在 Go 1.22 之前的版本中,執行此程式碼通常會導致多次列印相同的值(例如,c),而不是按預期列印values中的每個值(abc)。這是因為迴圈變數v在每次迭代中都被重複使用,而 goroutines 擷取了相同的記憶體位址。

Go 1.22 的修正

Go 1.22 引進了對此行為的修正,方法是使迴圈變數的行為如同在每次迭代中都被重新宣告一樣。這表示每個閉包現在都應該正確地擷取其對應迭代的v值。

但是,如果您已升級到 Go 1.22,但仍然觀察到舊的、有問題的行為,那麼罪魁禍首可能是您的go.mod檔案。只有當go.mod檔案指定 Go 版本為1.22或更高版本時,此修正才會生效。此相依性是有意的,允許開發人員在需要時保留與舊有行為的向後相容性。

在您的go.mod中指定go 1.22並應用修正後,執行上述程式應該會正確列印 a、b、c。每個 goroutine 現在都會擷取其迭代的正確v值。

經驗教訓

  • 保持更新:始終閱讀新 Go 版本的發行說明。Go 1.22 發行說明特別提到此更改及其對go.mod的相依性。
  • 使用正確的go.mod版本控制:許多 Go 語言功能都與go.mod中的go版本相關聯。如果您依賴新功能或修正,請確保指定正確的版本。
  • 徹底測試:即使在升級 Go 之後,也請透過執行全面的測試來確保您的程式按預期執行。

為什麼需要此相依性?

go.mod版本控制方法允許團隊逐步採用修正,而不會破壞現有的建置。透過將新的語言行為與go.mod中的go版本耦合,Go 團隊確保了向後相容性,同時也為開發人員提供了更順暢的遷移。

如果您遇到類似的挑戰,或對這如何影響您的工作流程有任何見解,請隨時在評論中分享!

BUG  GO  FOR RANGE  GO.MOD  GO 1.22 

       

  RELATED


  1 COMMENT


xieyonglin [Reply]@ 2024-11-21 06:43:57

Nice article?



  RANDOM FUN

Where should I go?