
go语言中for循环的迭代变量在goroutine闭包中会被共享,导致所有goroutine最终打印同一个值(即循环结束时的最终值),这是由变量作用域和闭包捕获机制引起的经典问题。
go语言中for循环的迭代变量在goroutine闭包中会被共享,导致所有goroutine最终打印同一个值(即循环结束时的最终值),这是由变量作用域和闭包捕获机制引起的经典问题。
在Go中,for range 循环中的迭代变量(如示例中的 l)在整个循环生命周期内复用同一内存地址,而非每次迭代创建新变量。因此,当启动多个 goroutine 并在其中直接引用 l 时,这些 goroutine 实际上都捕获了同一个变量的地址——而该变量的值在循环结束后已稳定为最后一次迭代的值(即 "go")。由于 goroutine 的执行具有异步性,几乎总是在循环完全结束后才真正执行 fmt.Println(l),此时 l 已是 "go",故输出五次 "go"。
✅ 正确做法:将当前迭代值显式传递给闭包,确保每个 goroutine 拥有独立的副本:
func main() {
langs := [5]string{"java", "python", "erlang", "cpp", "go"}
done := make(chan bool, 1) // 使用带缓冲通道避免阻塞
for _, l := range langs {
go func(lang string) { // 参数 lang 是每次迭代的独立拷贝
fmt.Println(lang)
}(l) // 立即传入当前 l 的值
}
// 等待所有 goroutine 完成(简单起见,此处可加 sync.WaitGroup 更健壮)
time.Sleep(100 * time.Millisecond) // 临时等待,仅用于演示
close(done)
}
⚠️ 注意事项:
- 不要依赖
chan bool单次接收来同步多个 goroutine(原代码只能接收一次,其余 goroutine 输出可能被丢弃或程序 panic); - 生产环境应使用
sync.WaitGroup精确等待所有 goroutine 结束; - Go 1.22+ 虽对
range循环变量做了部分语义优化(如for i := range s中i默认按值绑定),但闭包捕获行为未改变,显式传参仍是最清晰、兼容性最好的方案; - 若需在闭包中修改变量,也应传入指针或使用外部局部变量(需额外注意并发安全)。
总结:Go 中闭包捕获的是变量的引用,而非值;在循环中启动 goroutine 时,务必通过函数参数或声明内部变量(如 l := l)实现值的快照,才能保证预期行为。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











