You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:创建无缓冲messages Channel → 启动rtn goroutine → 进入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:54:21