什么 Go 的 for-range 循环变量在 goroutine 中会出问题?
Go 的 for-range 循环在遍历切片或数组时,循环变量(如 user)是复用的,而不是每次迭代都创建新的变量。这意味着:
循环变量是单例的:
for _, user := range users {
// user 是同一个变量,每次循环只是更新它的值
}
每次循环不会创建新的 user,而是修改同一个变量的值。
goroutine 的延迟执行问题:
当你这样写:
for _, user := range users {
go func() {
fmt.Println(user.Name) // 可能打印最后一个 user 的值!
}()
}
由于 goroutine 是异步启动的,很可能在它们执行时,for 循环已经跑完,此时所有 goroutine 看到的 user 都是最后一次迭代的值。
解决方法:
方法 1:传递循环变量的副本
for _, user := range users {
currentUser := user // 创建局部副本
go func() {
fmt.Println(currentUser.Name) // 正确
}()
}
方法 2:直接传参(推荐)
for _, user := range users {
go func(u User) {
fmt.Println(u.Name) // 正确
}(user) // 传值拷贝
}
方法 3:使用索引
for i := range users {
go func(idx int) {
fmt.Println(users[idx].Name) // 正确
}(i)
}
为什么 Go 这样设计?
性能优化:复用变量减少内存分配(但带来了并发陷阱)。
历史原因:早期 Go 的闭包设计没有考虑到 goroutine 的并发问题。
类似问题
JavaScript 的 var 循环变量(ES6 后用 let 解决)
C# 的 foreach 闭包问题(需手动拷贝变量)
结论:在 Go 里,for-range + goroutine 组合使用时,一定要小心循环变量的作用域问题!

浙公网安备 33010602011771号