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

关于Go语言Buffered Channel阻塞机制的困惑与问询

Go缓冲通道阻塞与调度疑问解答

问题1:为什么第一个示例代码未触发死锁?

你给出的示例代码:

package main

import "fmt"

func main() {
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

你对缓冲通道的阻塞条件理解存在偏差:发送到缓冲通道仅当缓冲区已满时才会阻塞。这里创建的是容量为2的缓冲通道,ch <- 1和ch <- 2执行时,缓冲区依次被填充到1和2(刚满),但这两个发送操作都没有触发阻塞——因为它们在同一个goroutine中顺序执行,执行完ch <- 2后,代码立刻进入接收语句,缓冲区被消费腾出空间,整个过程main goroutine从未进入阻塞等待状态,自然不会触发死锁。

死锁的核心触发条件是:goroutine无法推进执行,且没有其他goroutine能帮助解除阻塞。这个例子中main goroutine可以自行完成发送-接收的闭环,不存在阻塞等待的情况。

问题2:第二个代码的执行顺序差异与goroutine调度

你的测试代码:

chh := make(chan int, 2)

go func() {
    chh <- 1
    fmt.Printf("chh after 1: %v, %v\n", cap(chh), len(chh))
    chh <- 2
    fmt.Printf("chh after 2: %v, %v\n", cap(chh), len(chh))
    chh <- 3
    fmt.Printf("chh after 3: %v, %v\n", cap(chh), len(chh))
}()

fmt.Println(<-chh)
fmt.Println(<-chh)
fmt.Println(<-chh)

你观察到的本地与Go Playground输出差异,核心原因是Go goroutine的调度时机不固定,不同运行环境的调度器行为存在差异:

  • 当main goroutine执行第一个<-chh时,通道为空,main进入阻塞状态,调度器切换到子goroutine执行。
  • 子goroutine发送1到通道后,main goroutine被唤醒,但调度器不一定立刻切换回main,可能让子goroutine继续执行打印语句;也可能直接切换回main,让main先打印接收的值。
  • 后续的发送、接收与打印顺序,完全取决于调度器的动态切换策略——比如系统负载、时间片分配、通道操作的唤醒逻辑等,这些因素在不同环境下存在差异,导致输出顺序不同。

你觉得“子goroutine在chh <-1后立即阻塞”是误解,子goroutine并没有阻塞,只是调度器选择了切换回main goroutine执行接收操作,所以你看到main先打印了1,之后子goroutine才继续执行打印步骤。

内容的提问来源于stack exchange,提问作者Jimmy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 06:11:13