从main函数通过channel向多个goroutine发送值:谁先接收?
关于Go中多个Goroutine接收Channel值的顺序问题
嘿,我来帮你理清楚这个问题~首先先看你当前的代码,这里有个致命的死锁问题:你用的是无缓冲Channel,main函数里直接执行ch <- 1的时候,还没有任何Goroutine在等待接收这个Channel的值——你是先发送值,再调用receive函数启动Goroutine的,这会导致main Goroutine一直阻塞在发送操作上,程序直接死锁报错。
核心疑问:哪个Goroutine会最先收到Channel的值?
答案是:没有固定的确定性顺序,取决于两个因素:
- 如果你的Goroutine里有延迟操作(比如代码里的
time.Sleep),那哪个Goroutine先完成延迟、先执行到<-ch语句,哪个就会先收到值。 - 如果没有任何延迟,多个Goroutine都在等待同一个无缓冲Channel时,Go的Runtime调度器会随机选择一个Goroutine来完成接收操作——这个选择逻辑是Runtime内部实现的,我们没法提前预测。
修正后的代码示例
我给你调整了代码,解决死锁问题,同时能更直观地看到接收顺序:
package main import ( "fmt" "math/rand" "sync" "time" ) func main() { rand.Seed(time.Now().UnixNano()) // 初始化随机数种子,让每次Sleep的时间更随机 ch := make(chan int) var wg sync.WaitGroup // 先启动所有接收Goroutine for i := 0; i < 4; i++ { wg.Add(1) go func(goroutineID int) { defer wg.Done() // 随机延迟,模拟不同的执行节奏 time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) val, ok := <-ch if ok { fmt.Printf("Goroutine %d 收到了值: %d\n", goroutineID, val) } else { fmt.Printf("Goroutine %d 发现Channel已关闭\n", goroutineID) } }(i) } // 发送值到Channel ch <- 1 // 关闭Channel,避免未接收的Goroutine一直阻塞 close(ch) // 等待所有Goroutine执行完成,避免main提前退出 wg.Wait() }
补充说明
- 我用了
sync.WaitGroup来等待所有Goroutine执行完成,比time.Sleep更可靠,适合生产环境。 - 如果你去掉代码里的
time.Sleep,多次运行程序会发现每次接收的Goroutine ID都可能不一样,这就是Runtime调度器的随机性导致的。 - 不管是无缓冲还是有缓冲Channel,当多个Goroutine等待接收时,Go语言都没有保证固定的接收顺序。
内容的提问来源于stack exchange,提问作者Himanshu
相关产品推荐
相关产品推荐

