<p>无缓冲channel一用就死锁,因其要求发送和接收操作必须在不同goroutine中同时就绪;若只发不收、只收不发,或在同个goroutine中顺序执行ch <- v和<-ch,发送方将永久阻塞等待接收者。</p>

无缓冲channel为什么一用就死锁
因为无缓冲channel要求发送和接收必须同时就绪,任一方先执行就会永久阻塞。常见错误是只发不收、只收不发,或在同一个goroutine里顺序执行ch 和<code>——这相当于自己等自己。
典型踩坑场景:
- main goroutine 启动子goroutine后立刻
,但子goroutine还没走到<code>ch - 子goroutine 发完数据就退出,main还没来得及读,而channel未关闭,后续
永远卡住 - 多个goroutine往同一个无缓冲channel发数据,但只有一个接收方,第二个
ch 就会阻塞
解决办法:确保配对操作跨goroutine;用select加default防止单点阻塞;或改用带缓冲的channel缓解时序压力。
有缓冲channel的cap和len怎么影响行为
cap(ch)是缓冲区容量,决定最多能暂存几个值;len(ch)是当前已存值数量。两者共同决定发送是否阻塞:
立即学习“go语言免费学习笔记(深入)”;
- 当
len(ch) 时,<code>ch 立即返回 - 当
len(ch) == cap(ch)时,ch 阻塞,直到有人<code> -
len(ch)为0时,阻塞,直到有人写入
注意:len(ch)不是线程安全的“实时快照”,它只反映调用瞬间状态;不能靠轮询len(ch)来判断是否可读,应使用select配合default做非阻塞尝试。
channel关闭后还能不能读写
关闭后:ch 会panic,错误信息是<code>send on closed channel;不会panic,而是返回零值+<code>ok==false(如v, ok := 中<code>ok为false)。
关键约束:
- 只有sender(写端)能调用
close(ch),receiver调用会panic - 关闭前必须确保所有写操作已完成,否则可能漏数据或panic
- 关闭后仍可继续读取剩余数据,直到全部读完才开始返回零值
常见误用:在for-range循环里对channel调用close()——range自动检测关闭,手动关会导致panic。
用select处理多个channel时容易忽略什么
select本质是随机选择一个就绪case,不是按书写顺序优先级排队。如果多个case同时就绪,运行时随机挑一个执行,无法预测。
必须注意的细节:
- 没有
default分支时,select会阻塞,直到至少一个case就绪 - 有
default时,即使所有channel都不可读/不可写,也会立刻执行default,实现非阻塞尝试 -
time.After和context.WithTimeout常和select搭配做超时控制,但别重复创建timer,避免goroutine泄漏 - 不要在case里长时间执行逻辑,否则会拖慢整个select轮询节奏
最隐蔽的问题是:向已关闭的channel发送数据不会被select捕获为“可写”,而是直接panic——所以写操作前务必确认channel还开着。


















