无缓冲 channel 的阻塞本质是双向同步等待:发送和接收必须同时就绪才能完成,任一方未就绪即永久阻塞,形成 goroutine 间严格的 rendezvous 机制。

Go 里用 chan 做数据同步,关键不是“怎么传数据”,而是“什么时候能继续往下走”——它本质是阻塞点,不是管道。
无缓冲 channel 的阻塞行为怎么理解
无缓冲 channel(make(chan int))的发送和接收必须成对出现,否则任一端都会卡住。这不是“等对方准备好”,而是“双方必须同时就位”。
- 发送方执行
ch 时,会一直阻塞,直到有 goroutine 执行 <code> 且已准备好接收 - 接收方执行
时,也会一直阻塞,直到有 goroutine 执行 <code>ch 且已准备好发送 - 如果两个操作都在同一个 goroutine 里,比如先
ch 再 <code>,程序直接死锁——因为没有第二个 goroutine 来配合
有缓冲 channel 的“异步”到底异步在哪
有缓冲 channel(make(chan int, 5))只在缓冲区满或空时才阻塞,但它不改变同步语义:每次发送/接收仍是原子操作,只是多了暂存空间。
- 缓冲区未满时,
ch 立即返回,数据写入内部环形队列,不等待接收方 - 缓冲区已满时,
ch 阻塞,直到有接收动作腾出空间 - 缓冲区为空时,
阻塞;非空时立即返回队首元素(FIFO) - 注意:
len(ch)返回当前队列长度,cap(ch)返回缓冲容量,二者都只读,不能用于判断“是否安全发送”
关闭 channel 后接收行为的陷阱
关闭 channel(close(ch))后,发送会 panic,但接收不会——不过接收结果取决于 channel 类型和状态,容易误判。
立即学习“go语言免费学习笔记(深入)”;
- 对已关闭的无缓冲 channel,
立即返回零值 + <code>false(如0, false) - 对已关闭的有缓冲 channel,
先返回缓冲中剩余数据,全部取完后才开始返回零值 + <code>false - 错误写法:
if val := 判断是否还有数据——整数 0、空字符串、nil 指针都可能是合法值,不能靠零值判断通道是否关闭或空 - 正确做法:用双赋值
val, ok := ,<code>ok == false表示通道已关闭且无数据可取
select 中 default 分支如何避免忙等
select 是处理多个 channel 的核心语法,但没 default 容易阻塞,加了又可能变成轮询,得看场景选。
- 想“非阻塞尝试收发”:加
default,但要控制调用频率,否则 CPU 空转 - 想“超时控制”:用
time.After配合select,而不是循环 +default - 想“等待任意一个就绪”:去掉
default,让 goroutine 挂起,由运行时调度唤醒 - 常见坑:
select {}会让 goroutine 永久休眠,select { default: }会立即执行 default 分支,两者语义完全不同
真正难的不是记住 ch 和 <code> 的方向,而是预判每个操作在什么条件下阻塞、谁来解阻塞、阻塞时 goroutine 是否泄漏——这些细节藏在并发流的时序里,没法靠单次测试覆盖全。


















