移除quit通道发送语句后,Goroutine导致Go程序无法终止并报错的原因探究
关于Go Tour中Fibonacci通道代码的死锁问题解答
咱们先拆解下你遇到的问题,先梳理移除quit <- 0后的执行流程,再逐个解答你的疑问:
程序执行的核心流程
当你注释掉quit <- 0后,程序的执行步骤变成了这样:
- main函数创建
c和quit两个无缓冲通道,启动匿名goroutine。 - 匿名goroutine循环5次,每次从
c接收数据并打印,完成5次循环后,这个goroutine直接退出——它没有往quit发任何结束信号。 - main函数调用
fibonacci(c, quit),进入这个函数的无限循环+select逻辑。
为什么会出现「所有goroutine都处于休眠状态」的致命错误?
这是Go runtime的死锁检测机制在起作用。Go要求程序中的所有goroutine必须能持续推进,当runtime发现所有goroutine都处于无法继续执行的阻塞状态时,就会抛出这个致命错误。
具体到你的代码:
- 匿名goroutine已经执行完毕退出,不存在了。
fibonacci函数所在的goroutine:它的select一直在等待两个case中的一个能执行,但此时:- 第一个case
c <- x:无缓冲通道的发送操作必须要有对应的接收者才能完成,但c的唯一接收者(那个匿名goroutine)已经消失,所以这个发送操作会永久阻塞。 - 第二个case
<-quit:quit通道从来没有被写入过数据,也没有被关闭,所以这个接收操作也会永久阻塞。
- 第一个case
- main函数:它在等待
fibonacci函数返回,但fibonacci已经被阻塞住永远不会返回,所以main也处于阻塞状态。
此时整个程序的所有goroutine(main和fibonacci)都卡住了,没有任何一个能继续执行,runtime就判定为死锁,抛出错误。
「没有c通道的读取者,也没有更多代码语句需要执行」时实际发生了什么?
你说得没错,匿名goroutine确实已经完成了所有任务退出,但main函数和fibonacci goroutine还在运行:
- main函数还在执行
fibonacci(c, quit)这一行,它会一直等待fibonacci函数执行完毕返回,不会主动退出(哪怕main后面没有其他代码,它也会等子goroutine的函数调用完成)。 - fibonacci goroutine则被卡在select语句上:它尝试往
c发数据,但没人接收,又等不到quit的信号,所以完全停住,既无法执行x, y = y, x+y,也无法退出函数。
相当于两个goroutine各自卡住,整个程序彻底停摆。
如果quit通道永远不会触发,select的第一个case会一直无限循环吗?
不会的。无限循环的前提是select能不断执行第一个case,但这里的第一个case是c <- x——无缓冲通道的发送操作必须有接收者才能完成。当匿名goroutine退出后,c没有接收者了,这个发送操作就会阻塞,select会一直停在这个发送操作上,根本无法完成这个case的执行,自然也就不会执行后面的x, y = y, x+y,更不会进入下一次for循环。
简单说:select不是轮询两个case,而是等待任意一个case可以执行。当两个case都无法执行时,select就会一直阻塞,不会循环。
内容的提问来源于stack exchange,提问作者kwevelis
相关产品推荐
相关产品推荐

