go - 了解 Go channel 死锁

标签 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 条消息,然后给出“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/

相关文章:

bash - 使用带有 Go 的 Git Bash/MINGW64 在 Windows 上捕获 CTRL-C

go - 编码成 bson.Raw

Goland:运行任意代码质量工具,获得可点击的结果

amazon-web-services - 在 AWS Application Load Balancer 上使用 Go 时,WebSocket 连接失败并出现 301

go - 如何在 GO 中导出所有包 API 名称

Godoc 文档不输出列表

amazon-web-services - 从正在运行的 CLI Go 程序更新 AWS 凭证以访问不同的账户

go - 使用空接口(interface)或空结构作为映射的值有什么区别?

go - 如何在 go 中创建配对类型?

regex - Golang 中的正则表达式问题