什么 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 组合使用时,一定要小心循环变量的作用域问题!

posted @ 2025-07-08 20:25  灵火  阅读(35)  评论(0)    收藏  举报