Go语言中关闭Channel后接收方Goroutine为何永不阻塞?
Go Channel close函数的本质解释
先给你明确结论:close函数既不会启动goroutine,也不会持续往通道发送默认值,它的作用只是给通道打上一个「不再接受新发送值」的标记,后续的接收行为是Go语言对已关闭通道的逻辑设计。
核心逻辑拆解
- 通道关闭后,已发送到通道里的值依然可以被正常接收;当通道内所有值都被取完,后续的接收操作会立刻返回对应类型的零值,同时返回的
ok标志会变成false。这不是通道在“发值”,而是Go语言规定:对已关闭且空的通道执行接收,不会阻塞,直接返回零值+false。 - 没关闭通道时,当通道里的值被取完,再执行接收会陷入阻塞——因为Go认为可能还有发送方会往通道里塞值,主goroutine卡在select里无其他工作可做,就触发了死锁错误。
结合你的代码具体分析
修正转义符号后的正确代码如下:
import "fmt" import "time" func main(){ intChan := make(chan int, 1) strChan := make(chan string, 1) intChan <- 3 strChan <- "hello" fmt.Println("send") // 注释此处会触发致命错误:all goroutines are asleep - deadlock! close(intChan) close(strChan) for { select { case e, ok := <-intChan: time.Sleep(time.Second * 2) fmt.Println("The case intChan ok: ", ok) fmt.Println("The case intChan is selected.", e) case e, ok := <-strChan: time.Sleep(time.Second) fmt.Println("The case strChan ok: ", ok) fmt.Println("The case strChan is selected.", e) } } }
- 取消注释
close后:通道被标记为关闭,接收完3和hello后,后续每次select都会随机选中某个case(因为两个通道的接收都不会阻塞),返回对应类型的零值(int是0,string是空字符串)和false,所以循环会一直执行下去。 - 即使你一开始不往通道发值,直接close,接收操作同样会立刻返回零值+false,不会阻塞或报错——这依然是Go对已关闭通道的接收逻辑,和“发值”无关。
额外注意事项
- 只有发送方应该关闭通道,接收方关闭会直接触发panic;
- 往已关闭的通道发送值也会触发panic;
- 可以多次从已关闭的通道接收,每次都会得到零值+false。
内容的提问来源于stack exchange,提问作者Pzhang
相关产品推荐
相关产品推荐

