无缓冲channel是协程间最直接的同步点,因发送和接收必须同时就绪才能完成“手递手”交接,适合初始化等待等场景;应使用chan struct{}明确语义,避免主goroutine退出导致子goroutine永久阻塞。

无缓冲 channel 是最直接的同步点
协程间需要“等对方做完再继续”,无缓冲 chan 就是天然选择。它不存数据,只做握手:发送方阻塞,直到有接收方就绪;接收方也阻塞,直到有发送方送达。这种双向阻塞构成确定的执行顺序。
- 适合场景:启动一个 goroutine 执行任务,主线程必须等它结束才能继续(比如初始化、清理)
- 别写成
make(chan int, 0)——虽然等价,但语义模糊;直接用make(chan struct{})更清晰,因为不传数据,只传信号 - 常见错误:主 goroutine 没读取就退出,子 goroutine 永远卡在
ch 上,造成 goroutine 泄漏
接收端必须检查 ok 值,尤其在 for-range 中
channel 关闭后还能读,但每次返回零值和 false。如果不检查 ok,就会把零值当有效数据处理,逻辑出错。
-
for v := range ch自动处理关闭,但前提是发送方明确调用close(ch);若发送方不关,循环永不退出 - 手动接收时务必用
v, ok := ,<code>!ok表示 channel 已关闭且无剩余数据 - 多个 goroutine 同时向同一 channel 发送时,不能靠关闭来“通知所有人结束”——关闭只能由发送方执行一次,重复 close 会 panic
缓冲 channel 不是性能开关,而是语义表达
缓冲大小不是调优参数,而是你对“允许积压多少”的明确声明。设为 1 表示“最多一个待处理任务”,设为 100 表示“可容忍短暂排队”,跟吞吐量没直接关系。
- 不要为了“避免阻塞”盲目加大缓冲——这掩盖了生产/消费速率不匹配的问题,反而让问题延迟暴露
- 缓冲 channel 的发送只在满时阻塞,接收只在空时阻塞;它无法保证 goroutine 严格同步,仅解耦节奏
- 用
len(ch)查当前队列长度,cap(ch)查容量;但别依赖len做逻辑分支——它只是快照,瞬间可能变化
select 配合 channel 实现超时与非阻塞
单个 channel 操作会永久阻塞,而 select 让你能监听多个 channel,并控制等待行为。
- 加
default分支实现非阻塞尝试:select { case v := ,适合轮询或避免卡死 - 加
time.After实现超时:case ,注意不要在循环里反复创建 timer,应复用或用 <code>time.NewTimer - 别在
select里重复使用已为nil的 channel 变量——会导致该分支永远不可达,且不易排查
实际写的时候,最容易被忽略的是:channel 的关闭责任归属和生命周期管理。不是“谁先用完谁关”,而是“谁负责发送,谁负责关闭”。接收方永远不该 close,也不该假设关闭就等于“所有数据已到”——因为关闭前可能还有未发送的数据正在路上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











