<p>无缓冲 channel 一发就卡住是因为其容量为0,发送必须与接收goroutine实时配对握手,否则阻塞;常见死锁错误:fatal error: all goroutines are asleep - deadlock!</p>

无缓冲 channel 为什么一发就卡住?
因为它的容量是 0,不是“默认缓存 1”,也不是“小一点的缓冲”,而是**根本不存数据**——发送操作必须等到另一个 goroutine 正好在执行接收,两者在 runtime 层面“握手”成功,才能继续。没配对,就死等。
- 常见错误现象:
fatal error: all goroutines are asleep - deadlock!,比如只写ch := make(chan int); ch 就 panic - 典型场景:任务完成通知、goroutine 启动同步、信号量控制(如限制同时运行的 goroutine 数)
- 实操建议:调试时故意用无缓冲 channel,能快速暴露“谁忘了收”或“谁多发了”这类逻辑漏洞
有缓冲 channel 的阻塞点在哪?
阻塞只发生在两个时刻:发送时缓冲区已满,或 接收时缓冲区为空。中间所有操作都非阻塞——这才是它和无缓冲最本质的行为分界。
- 参数差异:
make(chan int, 0)等价于无缓冲;make(chan int, 1)是真正有 1 个槽位的缓冲通道 - 容易踩的坑:误以为
cap(ch) == 1就等于“最多收 1 次”,其实它允许你连续发 1 次不阻塞,但第 2 次才卡——而接收端哪怕还没动,第一次发送也已完成 - 性能影响:缓冲区越大,内存占用越明显;但过小(如
1)可能让生产者频繁等待,反而降低吞吐
怎么选?看通信双方节奏是否匹配
不是“高级功能用缓冲,基础功能用无缓冲”,而是看你要解决的是同步问题,还是解耦问题。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用无缓冲:需要严格顺序控制,比如 A 必须等 B 初始化完再开始;或者做轻量级信号(
done := make(chan struct{})) - 用有缓冲:生产者快、消费者慢(如日志采集 + 异步刷盘),或需要削峰(突发请求暂存通道中)
- 关键提醒:缓冲大小不是拍脑袋定的。设为
100不代表系统更稳,可能只是把 OOM 延后了——要结合平均处理耗时、峰值 QPS 和内存预算反推
关闭 channel 时,有无缓冲表现一样吗?
关闭行为本身一致:关闭后不能再发送,但可继续接收(已缓冲的数据或零值);但**关闭前的状态风险不同**。
立即学习“go语言免费学习笔记(深入)”;
- 无缓冲 channel 关闭后立刻接收会得零值 +
ok == false,安全 - 有缓冲 channel 关闭后还能读出剩余数据,但如果没人读,这些数据就永远卡在内存里,且无法被 GC 回收(底层
hchan结构持有引用) - 实操建议:只要用了缓冲 channel,就要确保接收端逻辑覆盖“通道关闭 + 缓冲未清空”的情况,别依赖超时或忽略
缓冲大小不是配置项,是并发契约的一部分——它隐式定义了“最多允许多少工作积压”。设错一个数字,可能让程序在低负载下飞快,在高负载下静默堆积然后崩掉。

















