有缓冲channel本质是解耦收发时机的固定容量队列,非提速工具;创建用make(chan T, cap),cap=0为无缓冲,cap>0启用缓冲,cap<0 panic;写满或读空时阻塞;关闭后可读尽剩余数据,再读返回零值和false。

有缓冲 channel 不是“更快的 channel”,而是用来解耦发送和接收时机的队列。它不会消除阻塞,只是把阻塞点从“必须立刻配对”挪到了“缓冲区满/空”时。
创建时填多少容量才不踩坑
填 0 就是无缓冲 channel;填负数会 panic:make: negative len;填正整数才启用缓冲。容量不是越大越好:
-
cap = 1是最安全的起点,适合“发一个、处理一个”的轻量解耦(如日志采集) - 若生产者短时 burst 明显(比如每秒 500 次写入,消费者平均只处理 300 次),可按差值设为
256或1024 - 避免设成
100000这类大值:内存线性增长、GC 压力上升、掩盖下游瓶颈,且len(ch)持续接近cap(ch)就是积压信号
向已满的缓冲 channel 写数据会发生什么
会立即阻塞当前 goroutine,直到有其他 goroutine 从该 channel 读走至少一个元素。这不是 bug,是 Go 的调度机制在起作用——但容易被误判为死锁:
- 常见原因:读端逻辑有分支跳过
<-ch,或用select但漏了default或超时分支 - 别靠
len(ch) == cap(ch)自旋判断是否可写——这是竞态快照,不可靠 - 更稳妥的做法是用
select+default做非阻塞尝试,或加time.After防止无限等待
关闭后还能不能读、怎么读才安全
关闭后仍可读取剩余数据,直到全部取完;之后再读返回零值 + false。但要注意:
立即学习“go语言免费学习笔记(深入)”;
- 关闭前必须确保所有 sender 已完成,否则会 panic:
send on closed channel - 接收方不能仅靠
ok == false判断“已无数据”,因为缓存清空前ok一直是true - 典型安全模式是 sender 调用
close(ch),receiver 用for v := range ch遍历剩余内容 - 多个 goroutine 并发写入时,关闭责任必须由写方统一承担(例如配合
sync.WaitGroup),读方绝不能 close
缓冲 channel 的关键不在“有没有缓”,而在于谁控制生命周期、谁承担背压、以及怎么观测积压——len(ch) 和 cap(ch) 要一起看,持续高水位就是系统在报警。


















