Với việc phát hành Go 1.22, nhóm Go đã giải quyết một vấn đề tồn tại lâu dài với phạm vi biến vòng lặp
for-range
, vốn là một cạm bẫy phổ biến đối với các nhà phát triển khi viết mã đồng thời. Cụ thể, vấn đề là biến vòng lặp trong vòng lặp for-range
được tái sử dụng qua các lần lặp, dẫn đến hành vi không mong muốn khi có sự tham gia của các closure.
Vấn đề trước Go 1.22
Hãy xem xét ví dụ sau:
package forange
import "fmt"
func Do() {
done := make(chan bool)
values := []string{"a", "b", "c"}
for _, v := range values {
go func() {
fmt.Println(v) // In ra các giá trị không mong muốn
done <- true
}()
}
for _ = range values {
<-done
}
}
Và có một tập tin go.mod như bên dưới
module playground
go 1.16
require github.com/ozgio/strutil v0.4.0
Trong các phiên bản Go trước 1.22, việc chạy mã này thường dẫn đến việc in ra cùng một giá trị (ví dụ: c
) nhiều lần thay vì in từng giá trị trong values
(a
, b
, c
) như mong đợi. Điều này là do biến vòng lặp v
được tái sử dụng qua các lần lặp, và các goroutine đã nắm giữ cùng một địa chỉ bộ nhớ.
Khắc phục trong Go 1.22
Go 1.22 đã giới thiệu một bản sửa lỗi cho hành vi này bằng cách làm cho biến vòng lặp hoạt động như thể nó được khai báo mới trong mỗi lần lặp. Điều này có nghĩa là mỗi closure giờ đây sẽ nắm giữ chính xác giá trị của v
cho lần lặp tương ứng của nó.
Tuy nhiên, nếu bạn đã nâng cấp lên Go 1.22 và vẫn quan sát thấy hành vi cũ, có vấn đề, thủ phạm có thể là tập tin go.mod
của bạn. Bản sửa lỗi chỉ có hiệu lực nếu tập tin go.mod
chỉ định phiên bản Go là 1.22
trở lên. Sự phụ thuộc này là có chủ đích và cho phép các nhà phát triển giữ lại khả năng tương thích ngược với các hành vi cũ nếu cần.
Với go 1.22
được chỉ định trong go.mod
của bạn và bản sửa lỗi được áp dụng, việc chạy chương trình trên sẽ in ra a, b, c một cách chính xác. Mỗi goroutine giờ đây nắm giữ giá trị chính xác của v
cho lần lặp của nó.
Bài học kinh nghiệm
- Luôn cập nhật: Luôn đọc ghi chú phát hành cho các phiên bản Go mới. Ghi chú phát hành Go 1.22 đã đề cập cụ thể đến thay đổi này và sự phụ thuộc của nó vào
go.mod
. - Sử dụng phiên bản
go.mod
chính xác: Nhiều tính năng ngôn ngữ Go được gắn với phiên bảngo
tronggo.mod
. Nếu bạn đang dựa vào các tính năng hoặc bản sửa lỗi mới, hãy đảm bảo phiên bản chính xác được chỉ định. - Kiểm thử kỹ lưỡng: Ngay cả sau khi nâng cấp Go, hãy đảm bảo rằng các chương trình của bạn hoạt động như mong đợi bằng cách chạy các bài kiểm tra toàn diện.
Tại sao sự phụ thuộc này là cần thiết?
Phương pháp phiên bản go.mod
cho phép các nhóm áp dụng các bản sửa lỗi một cách tăng dần mà không làm hỏng các bản build hiện có. Bằng cách liên kết hành vi ngôn ngữ mới với phiên bản go
trong go.mod
, nhóm Go đảm bảo khả năng tương thích ngược đồng thời cho phép quá trình di chuyển mượt mà hơn cho các nhà phát triển.
Nếu bạn đã gặp phải những thách thức tương tự hoặc có những hiểu biết về cách điều này đã tác động đến quy trình làm việc của bạn, hãy chia sẻ trong phần bình luận!
Nice article?