Golang中channel的demo为什么出错了?求分析原因
Golang中channel的demo为什么出错了?求分析原因 大家好,我是一个刚开始学习Go的新手。关于我的演示代码,我有一个疑问。我无法理解为什么我的输出会是那样。在我看来,输出应该是“1,2,3…9”。有人能帮我解释一下吗?万分感谢。
package main
import (
"fmt"
"sync"
)
func main() {
doOnve()
}
func doOnve() {
var once sync.Once
funBody := func() {
fmt.Println("Only once")
}
c := make(chan int)
for i:=0;i<10;i++{
go func() {
once.Do(funBody)
c <- i
}()
}
for i := 0; i < 10; i++ {
fmt.Println( <- c)
}
}
输出:

更多关于Golang中channel的demo为什么出错了?求分析原因的实战教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢!!! 👍
更多关于Golang中channel的demo为什么出错了?求分析原因的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢!!! 👍
Go语言中闭包的陷阱与常见错误 - Calhoun.io 请参考其中关于"在 for 循环中声明的变量是通过引用传递的"这一部分的开始。
TL;DR:始终通过函数参数将数据传递给 goroutine。
您代码中的 goroutine 是闭包,可以“看到”外部函数的变量。通常,在 goroutine 开始打印循环变量的值之前,循环已经结束。此时,循环变量已经设置为最终值。
将值作为适当的函数参数传递可以避免这种情况。
go func(val int) {
once.Do(funBody)
c <- val
}(i) // 注意这里的 i
(这篇文章对此有更详细的解释。)
你的代码问题在于goroutine闭包中捕获了循环变量i,这会导致所有goroutine共享同一个变量引用。同时,sync.Once确保了funBody只执行一次,但channel接收的顺序是不确定的。
以下是修正后的代码:
package main
import (
"fmt"
"sync"
)
func main() {
doOnce()
}
func doOnce() {
var once sync.Once
funBody := func() {
fmt.Println("Only once")
}
c := make(chan int)
for i := 0; i < 10; i++ {
go func(idx int) {
once.Do(funBody)
c <- idx
}(i) // 传递i的副本
}
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
}
关键修改:
- 将闭包函数改为接受参数
idx int,这样每个goroutine会获得i的独立副本 - 在goroutine调用时传入当前的
i值:(i) - 修复了函数名拼写错误(
doOnve改为doOnce)
输出特点:
- "Only once"只会打印一次(由
sync.Once保证) - 数字0-9会以随机顺序打印(goroutine执行顺序不确定)
- 所有数字都会打印,不会重复也不会丢失
原代码的问题:
- 所有goroutine共享同一个
i的引用,最终可能都收到循环结束后的值(通常是10) - goroutine启动和执行的时机不确定,导致输出结果不可预测
这是典型的Go并发编程中闭包捕获循环变量的常见问题。通过传递参数副本可以确保每个goroutine获得正确的值。

