了解 Go 通道死锁

  • 本文关键字:死锁 通道 Go 了解 go
  • 更新时间 :
  • 英文 :

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 条消息,然后给出"所有 go 例程都是睡眠 - 死锁错误"。我知道如果我关闭频道,错误就会消失。

我想了解的是,运行时如何知道代码将在通道上无限等待,并且没有其他东西会将数据发送到通道。

现在,如果我向main((函数添加一个额外的go例程..它不会抛出任何错误并继续等待通道。

go func() {
for {
time.Sleep(2 * time.Millisecond)
}
}()

那么这是否意味着.. go运行时只是在寻找正在运行的go例程的存在,该例程可能会将数据发送到通道中,因此不会引发死锁错误?

如果你想更深入地了解 Go 如何实现死锁检测,请查看代码中抛出"all goroutines are asleep - deadlock!"的位置: https://github.com/golang/go/blob/master/src/runtime/proc.go#L3751

看起来 Go 运行时对有多少个 go例程、有多少空闲以及有多少个休眠锁定(不确定通道 I/O 上的哪个睡眠会增加(保留了一些相当简单的会计核算。在任何给定时间(与运行时的其余部分一起序列化(,它只是做一些算术并检查all - idle - locked > 0是否......如果是这样,那么该计划仍然可以取得进展......如果它是 0,那么你肯定是死锁。

您可以通过无限循环阻止goroutine休眠来引入活锁(就像您在实验中所做的那样,显然运行时对计时器的睡眠处理方式不同(。在这种情况下,运行时将无法检测到死锁,并永远运行。

此外,我不确定运行时何时检查死锁 - 如果您有兴趣,进一步检查谁调用该checkdead()可能会在那里产生一些见解。

免责声明 - 我不是 Go 核心开发人员,我只是在电视上玩一个:-(

当通道和互斥体操作上的所有 goroutines 都被阻塞时,运行时会出现"所有 go 例程都是睡眠 - 死锁错误"错误。

睡眠 goroutine 不会阻止这些操作之一。没有僵局,因此没有恐慌。

最新更新