理解去渠道死锁

时间:2017-08-24 21:31:39

标签: 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条消息,然后给出一个&#34;所有go例程都是睡眠 - 死锁错误&#34;。我明白如果我关闭频道,错误就会消失。

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

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

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

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

2 个答案:

答案 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不会阻止其中一个操作。没有死锁,因此没有恐慌。