Go语言已关闭无数据Channel为何能在select中被接收?
为什么关闭的Channel在select中会持续触发计数?
这是个非常典型的Go Channel行为问题,咱们来拆解清楚:
核心原因:已关闭Channel的接收特性
在Go语言中,当你调用close(c)关闭一个Channel后:
- 所有后续对该Channel的接收操作会立即返回,返回对应类型的零值,且不会发生阻塞。
- 在
select语句中,只要某个case的操作是「立即就绪」的,就有机会被选中执行;对于已关闭的Channel来说,接收操作永远处于就绪状态。
对应到你的代码分析
你的代码里做了这几件事:
- 创建了两个无缓冲Channel
c1和c2,并立刻关闭了它们。 - 循环1001次(从1000到0),每次通过
select尝试从两个Channel中接收数据。
因为两个Channel都已经关闭,它们的接收操作都是随时就绪的,Go的select会伪随机地选择其中一个就绪的case执行。所以每次循环都会触发其中一个计数器增长,最终两个计数器的总和刚好是1001(513+488=1001),而各自的数值是随机分配的——你多运行几次代码,会发现c1Count和c2Count的数值每次都不一样。
额外验证小实验
如果修改代码,只关闭其中一个Channel:
package main import ( "fmt" ) func main() { c1 := make(chan interface{}) close(c1) c2 := make(chan interface{}) // 不关闭c2 var c1Count, c2Count int for i := 1000; i >= 0; i-- { select { case <-c1: c1Count++ case <-c2: c2Count++ } } fmt.Printf("c1Count: %d\nc2Count: %d\n ", c1Count, c2Count) }
运行后你会发现c1Count是1001,c2Count是0——因为未关闭的c2接收会阻塞,select只会选择就绪的c1 case。
内容的提问来源于stack exchange,提问作者Trần Kim Dự
相关产品推荐
相关产品推荐

