Go语言通道阻塞顺序疑问:代码输出为何与预期不一致
为什么无缓冲Channel的写入后goroutine没有立刻切换到main?
你的问题涉及Go调度器的行为和无缓冲Channel阻塞的细节——虽然官方文档明确说明无缓冲Channel的写入操作会阻塞直到对应的接收操作完成,但写入完成后,goroutine的执行权并不是立刻切换到接收方,这取决于调度器的调度决策,这就是导致输出和预期不符的原因。
先看你的代码:
package main func rtn(messages chan<- string) { defer close(messages) println("p1") messages <- "ping1" //for i := 0; i < 10000000; i++ { } println("p2") messages <- "ping2" } func main() { messages := make(chan string) go rtn(messages) for msg := range messages { println(msg) } }
我们一步步拆解执行流程:
- main goroutine:创建无缓冲
messagesChannel → 启动rtngoroutine → 进入for range循环等待接收数据(此时main阻塞)。 - rtn goroutine:执行
println("p1")→ 执行messages <- "ping1":此时没有接收方就绪,rtn阻塞。 - 当main goroutine进入接收操作时,
rtn的写入操作被唤醒并完成——关键就在这里:写入完成后,rtn回到可运行状态,Go调度器此时可能选择让rtn继续执行,而不是立刻切换回main。 - 于是
rtn继续执行println("p2")→ 执行messages <- "ping2":此时再次阻塞(main还未处理完第一个接收的后续逻辑)。 - 直到
rtn阻塞后,调度器才切换到main goroutine,main开始打印ping1→ 继续接收ping2并打印 → 因为Channel被关闭,循环结束。
这就解释了为什么实际输出是p1 p2 ping1 ping2,而不是你预期的p1 ping1 p2 ping2。
你可以通过取消注释那段空循环来验证:当messages <- "ping1"完成后,rtn会执行大量空循环,调度器会因为goroutine运行时间过长而抢占执行权,切换到main,此时main先打印ping1,等循环结束后rtn才打印p2,输出就会符合你的预期。
补充:println是无缓冲的标准输出操作,所以输出顺序完全对应代码的执行顺序,不存在输出缓冲导致的错乱。
内容的提问来源于stack exchange,提问作者Ratamir
相关产品推荐
相关产品推荐

