Go语言channel缓冲与阻塞原理详解

前言

channel 是 Go 语言 goroutine 之间通信的核心机制,很多新手写并发程序时会遇到 goroutine 阻塞、死锁、数据写不进去等问题,根源都是没搞懂无缓冲通道和有缓冲通道的阻塞规则。本篇结合代码讲清 channel 的缓冲模型与阻塞时机。

一、无缓冲通道(unbuffered channel)

ch := make(chan int)
go func() {
    ch <- 100 // 发送
}()
v := <-ch     // 接收

无缓冲通道容量为 0,发送方必须等到有接收方准备好才会完成发送,属于"同步握手"。如果只发不收,发送 goroutine 会永久阻塞。

二、有缓冲通道(buffered channel)

ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
// 再发第4个会阻塞,直到有人接收腾出位置

容量为 3 时,前 3 个元素先进缓冲区,发送方不会阻塞;缓冲区满了之后,发送方才会等待。接收方在缓冲区为空时阻塞。

三、阻塞与关闭的常见规则

  1. 向已关闭的 channel 发送数据会 panic;
  2. 从已关闭的 channel 接收会得到零值,ok 标识为 false;
  3. 关闭后再次关闭会 panic;
  4. 遍历 channel 用 range,channel 关闭后 range 自动退出。

四、常见踩坑

  • 忘记接收导致发送 goroutine 泄漏,程序卡住;
  • 缓冲容量设置过大失去解耦意义,过小又频繁阻塞;
  • 主 goroutine 提前退出,子协程数据没发完就被回收。

总结

无缓冲通道做同步通信,有缓冲通道做异步削峰。理解"缓冲区满则发送阻塞、空则接收阻塞"这两条规则,就能避开大部分 channel 死锁问题。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-18 07:30