Gochannel超时机制触发的(fatal error: all goroutines are asleep - deadlock!)
Posted XY丶YX
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gochannel超时机制触发的(fatal error: all goroutines are asleep - deadlock!)相关的知识,希望对你有一定的参考价值。
学习channel超时机制时,有下面这段代码
这一段内容详情可见:
http://c.biancheng.net/view/4361.html
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新开一个协程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(2 * time.Second):
fmt.Println("超时")
quit <- true
break
}
}
}()
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second)
}
<-quit
fmt.Println("程序结束")
}
之后,我把代码改了一下,把for循环睡的时间改成了3秒,想看看直接触发超时,结果就出问题了
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新开一个协程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(2 * time.Second):
fmt.Println("超时")
quit <- true
break
}
}
}()
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second * 3)
}
<-quit
fmt.Println("程序结束")
}
百度查了一下这个报错的原因:
在main goroutine线,期望从管道中获得一个数据,而这个数据必须是其他goroutine线放入管道的
但是其他goroutine线都已经执行完了(all goroutines are asleep),那么就永远不会有数据放入管道。
所以,main goroutine线在等一个永远不会来的数据,那整个程序就永远等下去了。
这显然是没有结果的,所以这个程序就说“算了吧,不坚持了,我自己自杀掉,报一个错给代码作者,我被deadlock了”
回过头来看改之后的代码,for循环中沉睡了3s,所以<-quit是还没有执行到的,也就是说quit通道的信息没有接收端。而协程中,由于沉睡触发的往quit通道发送信息就因为缺少接收端,所以出现上面的报错。
解决方法有两种:
1.把quit通道改为有缓存空间的,让它在还未接收时,信息能先发到缓存空间。也就是改为
quit := make(chan bool, 2)
这里for循环中有3次,但实验发现,缓存空间为2就行了。我猜测应该是第三次的时候,还未来的及执行发送信号,main主协程中就执行到了quit的接收端。
2.把quit通道的接收端也放在一个协程里面,这样就不受main逻辑的影响。下面展示本方法的代码。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新开一个协程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(2 * time.Second):
fmt.Println("超时")
quit <- true
break
}
}
}()
go func(){
for {
<-quit
fmt.Println("get quit")
}
}()
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second * 3)
}
//<-quit
fmt.Println("程序结束")
}
以上是关于Gochannel超时机制触发的(fatal error: all goroutines are asleep - deadlock!)的主要内容,如果未能解决你的问题,请参考以下文章
mongoengine中queryset触发网络访问机制剖析