首页 / Go 语言入门教程 / 缓冲 channel 与方向

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。