无缓冲 channel 一用就死锁,因其同步通信特性:发送方必须等待接收方就绪才完成发送,若无 goroutine 在另一端等待,即刻阻塞导致死锁。

无缓冲 channel 为什么一用就死锁?
因为无缓冲 channel 是同步通信:发送方必须等到有接收方就位,才会完成发送;反之亦然。没有 goroutine 在另一端等着,ch 就会永远卡住,触发 <code>fatal error: all goroutines are asleep - deadlock!。
- 典型错误:在单个 goroutine 里先
ch 再 <code> —— 顺序执行无法形成“双方同时就绪”,必死锁 - 正确做法:至少一个操作放在独立 goroutine 中,例如
go func() { ch ,主 goroutine 再 <code> - 调试技巧:加
fmt.Println("before send")和fmt.Println("after send"),如果只打印前一句,基本可断定卡在发送上
带缓冲的 channel 怎么设容量才合理?
缓冲区不是越大越好。容量为 n 的 channel(如 make(chan int, 10))本质是队列,它只缓解“发送方暂时没接收者”的阻塞,但不消除逻辑依赖。
- 设为 0 或不填第二参数 → 无缓冲,强同步,适合信号通知、任务交接等需严格时序的场景
- 设为小正整数(如 1~10)→ 适合生产者偶尔快于消费者,但整体节奏可控,比如日志批量投递
- 设为大数(如 1000+)→ 容易掩盖背压问题,内存占用上升,且接收方滞后时数据积压不可控,慎用
- 永远不要设成动态值或基于配置文件读取后直接传入
make—— 编译期确定容量更安全,运行时改不了
channel 关闭后还能不能读?怎么判断是否关了?
能读,但行为变了:关闭后的 channel 不再阻塞,每次 都立即返回零值 + <code>false(第二个返回值),比如 v, ok := 中 <code>ok 为 false 表示已关闭。
- 常见误用:关闭后继续
ch → panic: send on closed channel - 安全模式:用
select+ok判断,读完立刻把 channel 设为nil,后续该 case 就自动失效 - 别依赖
len(ch) == 0 && cap(ch) == 0判断是否关闭 —— 这只能说明空且无缓冲,和关闭状态无关
为什么 chan 和 <code> 类型要显式声明?
这是编译器帮你挡 bug 的第一道防线。函数参数声明为 ch chan,调用方传进来的 channel 就只能发不能收;反过来 <code>ch 只能收不能发。类型系统强制约束流向,避免误操作。
- 生产函数(producer)参数应为
ch chan,消费函数(consumer)应为 <code>ch - 传错类型会编译报错,比如把
传给期待 <code>chan 的函数 → cannot use … as … value in argument - 不写方向(仅
chan T)虽能用,但失去静态检查,尤其在大型 pipeline 中容易引发隐蔽的数据竞争
channel 看似简单,真正难的是理解“阻塞时机”和“关闭语义”的耦合关系。很多人卡在 select 超时、nil channel 判定、或多个 goroutine 共用一个 channel 时的竞态上 —— 这些都不是语法问题,而是对通信模型的直觉没建立起来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











