Fix for-range Issue Again in Go 1.22

English 简体中文 繁体中文 ภาษาไทย Tiếng Việt
Summary

Go 1.22 resolves a long-standing for-range loop variable scoping issue that commonly caused unexpected behavior in concurrent code utilizing closures. Previously, the loop variable was reused across iterations, leading goroutines to capture the same memory address and often print the final value multiple times. The fix ensures the loop variable behaves as if declared fresh in each iteration, allowing closures to correctly capture the intended value. This crucial change only activates if the go.mod file specifies Go 1.22 or higher, a design decision that preserves backward compatibility while enabling new language features.

ด้วยการเปิดตัว Go 1.22 ทีม Go ได้แก้ไขปัญหาที่ยืนยงมาอย่างยาวนาน เกี่ยวกับขอบเขตตัวแปรวนซ้ำ for-range ซึ่งเป็นกับดักทั่วไปสำหรับนักพัฒนาที่เขียนโค้ดแบบขนาน โดยเฉพาะอย่างยิ่ง ปัญหาคือตัวแปรวนซ้ำในลูป for-range นั้นถูกนำกลับมาใช้ใหม่ในแต่ละรอบ ทำให้เกิดพฤติกรรมที่ไม่คาดคิดเมื่อมีการใช้ closures

ปัญหา ก่อน 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 (a, b, c) ตามที่คาดไว้ นี่เป็นเพราะตัวแปรวนซ้ำ v ถูกนำกลับมาใช้ใหม่ในแต่ละรอบ และ goroutines ได้จับที่อยู่หน่วยความจำเดียวกัน

การแก้ไข Go 1.22

Go 1.22 ได้แนะนำการแก้ไขพฤติกรรมนี้โดยทำให้ตัวแปรวนซ้ำทำงานราวกับว่ามันถูกประกาศใหม่ในแต่ละรอบ ซึ่งหมายความว่าแต่ละ closure ควรจับค่าของ v สำหรับการวนซ้ำที่ตรงกันได้อย่างถูกต้อง

อย่างไรก็ตาม หากคุณอัปเกรดเป็น Go 1.22 แล้วและยังคงเห็นพฤติกรรมเก่าที่เป็นปัญหาอยู่ ผู้กระทำผิดอาจเป็นไฟล์ go.mod ของคุณ การแก้ไขจะมีผลก็ต่อเมื่อไฟล์ go.mod ระบุเวอร์ชัน Go เป็น 1.22 หรือสูงกว่า การพึ่งพานี้เป็นเจตนาและอนุญาตให้นักพัฒนาคงความเข้ากันได้แบบย้อนหลังกับพฤติกรรมเก่าได้หากจำเป็น

เมื่อระบุ go 1.22 ใน go.mod ของคุณและมีการใช้การแก้ไข การรันโปรแกรมข้างต้นควรพิมพ์ a, b, c ได้อย่างถูกต้อง แต่ละ goroutine ตอนนี้จับค่าที่เหมาะสมของ v สำหรับการวนซ้ำของมัน

บทเรียนที่ได้รับ

  • อัปเดตอยู่เสมอ: อ่านบันทึกย่อประจำการเผยแพร่สำหรับเวอร์ชัน Go ใหม่เสมอ บันทึกย่อประจำการเผยแพร่ Go 1.22 กล่าวถึงโดยเฉพาะ การเปลี่ยนแปลงนี้และการพึ่งพา go.mod
  • ใช้เวอร์ชัน go.mod ที่ถูกต้อง: คุณลักษณะภาษา Go หลายอย่างถูกผูกไว้กับเวอร์ชัน go ใน go.mod หากคุณกำลังพึ่งพาคุณลักษณะหรือการแก้ไขใหม่ โปรดตรวจสอบให้แน่ใจว่าได้ระบุเวอร์ชันที่ถูกต้อง
  • ทดสอบอย่างละเอียด: แม้หลังจากอัปเกรด Go แล้ว โปรดตรวจสอบให้แน่ใจว่าโปรแกรมของคุณทำงานได้ตามที่คาดไว้โดยการรันการทดสอบที่ครอบคลุม

เหตุใดการพึ่งพานี้จึงจำเป็น

วิธีการเวอร์ชัน go.mod ช่วยให้ทีมต่างๆ สามารถนำการแก้ไขมาใช้ได้ทีละน้อยโดยไม่ทำลายการสร้างที่มีอยู่ โดยการเชื่อมโยงพฤติกรรมภาษาใหม่กับเวอร์ชัน go ใน go.mod ทีม Go จึงมั่นใจได้ถึงความเข้ากันได้แบบย้อนหลังในขณะที่ช่วยให้การโยกย้ายสำหรับนักพัฒนาเป็นไปอย่างราบรื่นยิ่งขึ้น

หากคุณเคยประสบกับความท้าทายที่คล้ายคลึงกันหรือมีข้อมูลเชิงลึกเกี่ยวกับวิธีการที่ส่งผลกระทบต่อเวิร์กโฟลว์ของคุณ โปรดแบ่งปันในความคิดเห็น!

BUG GO FOR RANGE GO.MOD GO 1.22

  RELATED

  COMMENT

1
xieyonglin
Nov 21, 2024 at 6:43 am

Nice article?