缓冲channel是容忍生产消费节奏错位的有界队列,非提速开关;cap必须为编译期常量且≥0,cap=0等价无缓冲,满/空时仍阻塞,关闭后可读尽剩余数据再返回零值和false。

缓冲 channel 不是“提速开关”,而是用来容忍生产者和消费者节奏错位的有界队列。它不会消除阻塞,只把阻塞点从「必须立刻配对」移到「缓冲满/空」——用错容量或误判行为,反而会放大死锁、积压、OOM 风险。
make(chan T, cap) 的 cap 必须是编译期常量
你不能写 make(chan int, n)(n 是变量),Go 编译器会直接报错。cap 只能是字面量整数,比如 1、64、1024。
-
cap = 0等价于无缓冲 channel:make(chan int, 0)和make(chan int)行为完全一致 cap 不会在编译时报错,但运行时 panic:<code>make: negative len- cap 一旦设定就不可变:没有 resize、没有扩容、没有运行时调整
向已满的缓冲 channel 写数据会立即阻塞
比如 ch := make(chan string, 2),连续两次 ch 和 <code>ch 成功,第三次 <code>ch 就会挂起当前 goroutine,直到有其他 goroutine 执行 <code> 拿走至少一个元素。
- 这不是 bug,是 Go 调度机制在起作用;但容易被误判为死锁——尤其当读端逻辑有分支跳过
,或 <code>select漏了default或超时分支 - 别靠
len(ch) == cap(ch)自旋判断是否可写:这是瞬时快照,竞态下毫无意义,还浪费 CPU - 更稳妥的做法是用
select+default做非阻塞尝试,或加time.After防止无限等待
关闭后还能读,但必须按正确模式处理
关闭后,已入队但未被读取的数据仍可正常取出;全部读完后,再读才返回零值和 false。
- sender 关闭前必须确保所有写操作已完成,否则会 panic:
send on closed channel - receiver 不能仅靠
ok == false判断“刚关”或“已无数据”,因为缓存清空前ok一直是true - 最安全的接收模式是
for v := range ch—— 它隐式处理了“读尽即停”,且不依赖len(ch)或手动检查ok - 多个 goroutine 并发写时,关闭责任必须由写方统一承担(如配合
sync.WaitGroup),receiver 绝对不能调用close(ch)(Go 明确禁止)
真正难的从来不是 make(chan T, cap) 这一行代码,而是谁决定关、什么时候关、下游积压到什么程度该告警——这些没法靠缓冲区自动解决。持续观察 len(ch) 和 cap(ch) 的比值,才是判断系统是否开始喘不过气的关键信号。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











