★ channel 关闭之后收发会怎样?range 与单向 channel 有哪些注意点?

结论先行

channel 只应由“发送方”负责关闭,close 是广播信号而非数据通道的终点:关闭后接收方取完缓冲里的数据,会持续读到零值(用 ok 或 range 识别关闭);而向已关闭 channel 发送、或再次 close 都会 panic。range 会自动处理 ok 判断,读到关闭即退出循环。

要点

  • 关闭职责:只有一个发送方就由它 close;多个发送方不要直接 close,用 sync.Once 或专门的“退出信号 channel”协调。
  • 关闭后接收:缓冲数据仍可取完;之后接收立即返回元素零值,v, ok := <-ch 的 ok 变为 false。
  • 关闭后发送:panic(send on closed channel);对已关闭 channel 重复 close 同样 panic——close 必须恰好执行一次。
  • range 遍历:for v := range ch 持续读取,直到 channel 关闭且缓冲取空后自动退出,等价于带 ok 的循环,是最常用的消费写法。
  • 单向 channel:chan<- T(只写)与 <-chan T(只读)是编译期约束;双向 channel 可隐式转为单向,反向不行。
  • 单向的意义:函数签名用单向类型能约束调用方“你只能写/只能读”,从类型上防止误 close、误接收。
  • nil channel:select 中永不就绪,可用于动态禁用某个 case;对 nil channel 直接收发或 range 会永久阻塞。

示例

jobs := make(chan int, 3)
go func() {
	for i := 0; i < 3; i++ {
		jobs <- i
	}
	close(jobs) // 只有发送方关闭
}()

for j := range jobs { // 读到关闭自动退出,无需 ok
	fmt.Println(j)
}
v, ok := <-jobs
fmt.Println(v, ok) // 0 false:已关闭且取空

func produce(out chan<- int) { out <- 1 } // 签名只写
func consume(in <-chan int)  { <-in }     // 签名只读

常见追问/记忆点

  • 追问:接收方可以 close 吗?答:语法允许但几乎总是错误:发送方可能还在发送而触发 panic;约定发送方关闭。
  • 追问:如何通知多个接收方“停止”?答:close 一个专门的 done channel,接收方 select 到它即退出,天然广播。
  • 追问:怎么判断 channel 是否已关闭?答:只能用接收的 ok 或 range 判断,没有“查询是否关闭”的原子 API。
  • 记忆:close 是一次性的“停止供货”信号;取完即零值;向已关闭发送与重复 close 必 panic。
笔记加载中…