package main
import (
"fmt"
"time"
)
func main() {
p := producer()
for c := range p {
fmt.Println(c)
}
}
func producer() <-chan string {
ch := make(chan string)
go func() {
for i := 0; i < 5; i++ {
ch <- fmt.Sprint("hello", i)
time.Sleep(1 * time.Second)
}
// commented the below to show the issue
// close(ch)
}()
return ch
}
运行上面的代码将打印5条消息,然后给出一个&#34;所有go例程都是睡眠 - 死锁错误&#34;。我明白如果我关闭频道,错误就会消失。
我想了解的是,运行时如何知道代码将在通道上无限等待,并且没有其他任何东西可以将数据发送到通道中。
现在如果我向main()函数添加一个额外的go例程..它不会抛出任何错误并继续等待通道。
go func() {
for {
time.Sleep(2 * time.Millisecond)
}
}()
这是否意味着.. go运行时只是在寻找可能将数据发送到通道的运行go例程,因此不会抛出死锁错误?
答案 0 :(得分:5)
如果您想更深入地了解Go如何实现死锁检测,请查看引发"all goroutines are asleep - deadlock!"
的代码中的位置:https://github.com/golang/go/blob/master/src/runtime/proc.go#L3751
看起来Go运行时保留了一些相当简单的计算方法,包括有多少goroutine,有多少空闲,以及有多少人正在为锁而睡觉(不确定哪个睡眠在通道I / O上会增加)。在任何给定的时间(与运行时的其余部分序列化),它只是做一些算术并检查是否all - idle - locked > 0
......如果是,那么程序仍然可以取得进展......如果它是0,那么你'肯定是陷入僵局。
你可以通过阻止goroutine通过无限循环进行睡眠来引入活锁(就像你在实验中所做的那样,显然睡眠对于计时器在运行时不会被视为相同)。在这种情况下,运行时将无法检测到死锁,并且会永远运行。
此外,我不确定运行时到底是什么时候检查死锁 - 如果您有兴趣,可以进一步检查checkdead()
可能会产生一些洞察力的人。
免责声明 - 我不是Go核心开发者,我只是在电视上播放: - )
答案 1 :(得分:4)
当所有goroutine在通道和互斥操作上被阻塞时,运行时出现“所有go例程都是睡眠 - 死锁错误”错误。
睡觉的goroutine不会阻止其中一个操作。没有死锁,因此没有恐慌。