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 条消息,然后给出“all go routines are a sleep - deadlock error”。我知道如果我关闭 channel ,错误就会消失。
我想了解的是,go runtime 如何知道代码将在 channel 上无限等待,并且没有其他任何东西可以将数据发送到 channel 中。
现在,如果我向 main() 函数添加一个额外的 go 例程......它不会抛出任何错误并继续等待 channel 。
go func() {
for {
time.Sleep(2 * time.Millisecond)
}
}()
这是否意味着 ..go 运行时只是在寻找正在运行的 go 例程的存在,该例程可能会将数据发送到 channel 中,因此不会引发死锁错误?
最佳答案
如果您想更深入地了解 Go 如何实现死锁检测,请查看代码中抛出 “all goroutines are asleep - deadlock!”
的位置:https://github.com/golang/go/blob/master/src/runtime/proc.go#L3751
看起来 Go 运行时对有多少 goroutines、有多少 goroutines 空闲以及有多少 goroutines 正在为锁休眠(不确定 channel I/O 上的 sleep 会增加)进行一些相当简单的统计。在任何给定时间(与运行时的其余部分一起序列化),它只是做一些算术并检查是否 all - idle - locked > 0
... 如果是这样,那么程序仍然可以取得进展。 .. 如果它是 0,那么你肯定陷入僵局。
您可以通过无限循环防止 goroutine 休眠来引入活锁(就像您在实验中所做的那样,显然运行时不会对定时器的休眠进行同样的处理)。在这种情况下,运行时将无法检测到死锁,并永远运行。
此外,我不确定运行时检查死锁的确切时间 - 如果您有兴趣,进一步检查调用 checkdead()
的人可能会在那里产生一些见解。
免责声明 - 我不是 Go 核心开发人员,我只是在电视上玩一个 :-)
关于go - 了解 Go channel 死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45871203/