【问题标题】:understanding go channel deadlocks了解 Go 通道死锁
【发布时间】:2018-02-02 21:39:03
【问题描述】:
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 条消息,然后给出“所有 goroutines are a sleep - deadlock error”。我知道如果我关闭频道,错误就会消失。

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

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

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

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

【问题讨论】:

    标签: go


    【解决方案1】:

    如果您想更深入地了解 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 &gt; 0...如果是,那么程序仍然可以取得进展...如果它是0,那么你肯定陷入僵局。

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

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

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

    【讨论】:

    • 感谢@Eddy R 提供的信息。尝试给 +1,但由于我的分数低,系统不允许。
    【解决方案2】:

    当所有 goroutine 在通道和互斥操作上被阻塞时,运行时会出现“所有 goroutine 都是睡眠 - 死锁错误”错误。

    休眠的 goroutine 不会阻塞这些操作之一。没有死锁,因此没有恐慌。

    【讨论】:

      猜你喜欢
      • 2020-01-24
      • 2019-05-10
      • 2017-06-24
      • 1970-01-01
      • 2021-12-25
      • 1970-01-01
      • 2017-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多