You need to enable JavaScript to run this app. 为何channel阻塞时,带default的select会触发goroutine死锁报错
优惠活动
大模型
产品
解决方案
定价
更多

为何channel阻塞时,带default的select会触发goroutine死锁报错

问题根本原因拆解

核心认知偏差点

你对通道类型的行为判断存在两个关键误区,直接导致了执行结果和预期不符:

  • 混淆了无缓冲通道和“写满的有缓冲通道”的本质差异
    • 代码中c := make(chan int)创建的是无缓冲通道,通道容量为0,从创建起就没有任何可存储值的空间,不存在“写满”这个状态前提。
    • 你认知中“通道写满触发发送阻塞”的场景,仅适用于有缓冲通道:当通道内已存值的数量等于初始化时指定的容量,后续发送操作才会进入阻塞。
  • 误判了前置独立发送语句的执行逻辑
    • 无缓冲通道的发送操作必须匹配到同时执行的接收操作才能完成,没有接收者的前提下,独立的发送语句本身就会陷入永久阻塞,根本不会流转到后续的select代码块。

实际执行链路还原

你写的代码根本没机会执行到select分支判断环节,完整流程如下:

  1. 初始化创建无缓冲通道c
  2. 执行c <- 1:尝试向无缓冲通道发送值1,此时全程序没有任何其他goroutine在等待接收这个通道的值,主goroutine直接在这行陷入永久阻塞
  3. Go运行时检测到当前唯一的主goroutine处于休眠状态,没有任何可唤醒它的执行逻辑,直接抛出所有goroutine均处于休眠状态——死锁!的错误

select default分支的触发逻辑澄清

你对select的行为判断本身没有错误:当select代码块顺利执行到判断逻辑时,所有case对应的通道操作都无法立刻完成,就会触发default分支。
但这个逻辑生效的前提是,select本身能被执行到。你的场景中前置的独立发送语句已经把主goroutine卡死,根本走不到select的判断环节,自然不可能命中default分支。

符合你预期效果的对照实现

如果要复现“通道写满、select发送阻塞、命中default分支”的逻辑,需要显式创建有缓冲通道,预先将通道占满:

func main() {
    // 创建容量为1的有缓冲通道
    c := make(chan int, 1)
    // 存入1个值,此时通道容量占满,无剩余存储空间
    c <- 1

    select {
        case c <- 5:
            fmt.Println("a")
        default:
            // 通道已占满,发送操作无法立刻完成,命中default分支
            fmt.Println("only case not blocked")
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 07:54:01