无缓冲 channel 一发就卡住是因为其容量为0,发送必须与接收goroutine严格配对、同时就绪,否则立即永久阻塞并触发死锁panic。

无缓冲 chan 本质是同步点,不是“管道”,用错就死锁;带缓冲 chan 的容量不是性能优化开关,而是解耦节奏的调节阀。
为什么 make(chan int) 一发就卡住?
这是最常踩的坑:无缓冲 channel 要求发送和接收**严格配对、同时就绪**。只要一方先动,另一方没跟上,goroutine 就永久阻塞,最终 panic “all goroutines are asleep”。
-
ch := make(chan int)创建的是同步通道,ch 不会返回,直到有另一个 goroutine 执行 <code> - 主 goroutine 直接写
ch (没起 goroutine 去收),程序立刻死锁 - 哪怕只差毫秒——比如 sender 先执行、receiver 还在调度队列里——也会阻塞,这不是竞态,是设计强制
make(chan int, N) 的 N 到底设多大?
缓冲区大小不是越大越好,也不是凭感觉填个 10 或 100。它直接决定你是在做“异步缓存”还是“背压泄漏”。
- N = 0 等价于无缓冲,行为完全一致
- N = 1 可破除简单生产-消费时序依赖,比如通知类信号(类似 mutex.Unlock)
- N > 1 仅在明确需要暂存多条数据时才合理,例如日志批量收集、任务预取队列
- 若 N 过大(如设为 10000),可能掩盖下游处理慢的问题,导致内存持续增长,OOM 风险上升
什么时候该用无缓冲,什么时候必须加缓冲?
选型依据不是“要不要快”,而是“通信是否需要解耦时序”。
立即学习“go语言免费学习笔记(深入)”;
- 用无缓冲:
sync.WaitGroup替代场景(如等待某操作完成)、协程间握手(启动/停止信号)、要求强顺序保证的控制流 - 用带缓冲:
taskChan := make(chan *Task, 10)这类生产者消费者明显速率不匹配的场景;或避免因 receiver 暂时不可达导致 sender 崩溃(如监控打点上报) - 特别注意:即使加了缓冲,
close(ch)后仍可读完剩余数据,但再写会 panic —— 关闭前务必确认 sender 不再发
缓冲区不是魔法开关,它是你对“谁等谁”“等多久”“能存几条”的显式声明。漏掉这个意识,chan 就从同步利器变成死锁陷阱。


















