无缓冲 channel 一发就阻塞是因为其设计为同步点,发送操作必须等待接收方就绪才能完成,否则 goroutine 立即挂起;这是 Go “通过通信共享内存”的核心机制,非 bug 而是强制同步协作的语言契约。

Go里用chan做同步数据流,关键不是“能不能传数据”,而是“谁在等、等什么、什么时候放行”。它天然把通信和同步绑在一起,错用就会卡死或panic。
无缓冲Channel为什么一发就阻塞?
因为它是同步点:发送操作ch 会一直挂起,直到有goroutine在另一端执行<code>。这不是bug,是设计——它强制双方节奏对齐。
- 适合精确协同场景,比如“任务启动→执行→完成通知”这种三段式流程
- 若只发不收(或只收不发),程序必然deadlock,运行时报
fatal error: all goroutines are asleep - deadlock - 别把它当队列用;想缓存数据,必须显式指定缓冲大小,比如
make(chan int, 10)
有缓冲Channel的“缓冲区满”到底指什么?
make(chan T, n)里的n是缓冲槽位数,不是字节数或元素个数上限——每个槽只能存一个T值。所谓“满”,就是qcount == dataqsiz,此时再ch 就会阻塞。
- 缓冲区为空时,接收操作
也会阻塞(除非channel已关闭) - 缓冲区未满≠能无条件发送:如果接收端已关闭channel,再发就会panic:
panic: send on closed channel - 常见误用:把缓冲大小设得过大(如
make(chan int, 1e6)),实际只是把内存压力从goroutine调度转移到堆上
range遍历Channel时,为什么必须有人close?
for v := range ch本质是持续接收,直到channel被关闭且缓冲区清空。它不关心谁关的、什么时候关的,只依赖closed标志位和qcount计数。
立即学习“go语言免费学习笔记(深入)”;
- 关闭规则必须明确:只有发送方能
close(ch);接收方close会直接panic - 多个发送方时,需用
sync.WaitGroup或额外信号channel协调关闭时机,否则可能漏数据或重复close - 没close就range,goroutine永远卡在循环里,无法退出
select + default为什么会跳过阻塞操作?
select里加default分支,等于告诉Go:“当前所有channel都不可操作时,别等,直接执行default”。这常用于非阻塞探测或超时兜底。
- 没有
default时,select必阻塞,直到至少一个case就绪 - 有
default时,哪怕所有channel都阻塞,也会立刻走default——但这也意味着你放弃了等待,要自己处理“无数据可取”的逻辑 - 典型陷阱:在worker循环里滥用
select { case job := ,实际是轮询+忙等,不如用带超时的<code>time.After或context
真正难的不是语法,是判断“这里该同步还是该异步”“谁该负责关闭”“缓冲大小是否掩盖了背压问题”。这些决策藏在业务节奏里,而不是make那一行代码中。


















