Go 语言入门教程
缓冲 channel 与方向
本教程共 80 篇 · 第 57 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
GoGo 入门教程channel缓冲channel单向channel并发caplen
57. 缓冲 channel 与方向
本节目标:掌握缓冲 channel 的容量概念,学会用
cap/len查看,理解单向 channel 的用途。
缓冲 channel
给 make 传第二个参数就是缓冲大小:
ch := make(chan int, 3) // 缓冲区能存 3 个 int
缓冲 channel 像一个队列。发送时先往队列里塞,塞满了才阻塞;接收时从队列里取,空了才阻塞。
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
// ch <- 4 // 这里会阻塞,因为缓冲区满了
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
}
和无缓冲的区别:无缓冲必须立刻有人接,缓冲的可以先存着。所以下面的代码不会死锁:
ch := make(chan int, 1)
ch <- 100 // 不阻塞,存进缓冲区了
fmt.Println(<-ch) // 100
cap 和 len
cap 返回缓冲区容量,len 返回当前缓冲区里有几个元素:
ch := make(chan int, 5)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(len(ch), cap(ch)) // 3 5
Warning
len只反映某一瞬间的状态。并发程序里你读到len(ch) == 0后,下一刻可能就有数据了。别拿len做判断逻辑,该阻塞就阻塞。
缓冲满了怎么办
满了再发送就阻塞,等其他 goroutine 来取数据腾位置:
func main() {
ch := make(chan int, 2)
go func() {
for i := 0; i < 5; i++ {
ch <- i
fmt.Println("发送:", i)
}
close(ch)
}()
for v := range ch {
fmt.Println("接收:", v)
time.Sleep(time.Millisecond * 100)
}
}
发送方一口气塞 2 个就满了,得等接收方取走一个才能继续。
缓冲区大小怎么选
- 大小为 0(无缓冲):强同步,收发双方必须同时在场
- 大小为 1:常用于信号通知
- 较大缓冲:削峰填谷,适合生产消费速度不均的场景
Tip缓冲不是越大越好。太大会掩盖问题:你以为程序正常跑,其实背地里积压了一堆数据没处理。先用小缓冲,发现不够再加。
单向 channel
channel 可以限制方向,只读或只写:
var readOnly <-chan int // 只能接收
var writeOnly chan<- int // 只能发送
规则:箭头 <- 在 chan 前面是只读,在后面是只写。
为什么要单向
双向 channel 可以自动转成单向,反过来不行。单向主要用于函数参数,限制别人能做什么:
// 只允许发送,不允许接收
func producer(ch chan<- int) {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
}
// 只允许接收,不允许发送
func consumer(ch <-chan int) {
for v := range ch {
fmt.Println(v)
}
}
func main() {
ch := make(chan int) // 双向
go producer(ch) // 双向自动转只写
consumer(ch) // 双向自动转只读
}
close 函数的参数就是 chan<- Type,只写类型。time.After 的返回值是 <-chan Time,只读类型。
Note单向 channel 是编译期的约束。你很少手动创建单向 channel 变量,更多是在函数签名里用它来约束行为,让代码更安全。
小结
make(chan T, n)创建容量为 n 的缓冲 channel- 缓冲满则发送阻塞,缓冲空则接收阻塞
cap看容量,len看当前元素数(别用来做逻辑判断)<-chan T只读,chan<- T只写- 双向可转单向,单向不能转双向
- 单向主要用于函数参数,约束操作权限
下一节学 select,它能同时监听多个 channel。