为什么通道上的 go-routine 块被认为是死锁?

ove*_*nge -2 deadlock go goroutine

根据这里的定义,死锁与资源争用有关。

在操作系统中,当一个进程或线程进入等待状态时会发生死锁,因为请求的系统资源被另一个等待进程持有,而另一个等待进程又在等待另一个等待进程持有的另一个资源。如果一个进程不能无限期地改变它的状态,因为它请求的资源正在被另一个等待的进程使用,那么系统就被称为死锁。


在下面的代码中:

package main

import "fmt"

func main() {
    c := make(chan string)

    c <- "John"
    fmt.Println("main() stopped")

}
Run Code Online (Sandbox Code Playgroud)

main() go-routine 阻塞,直到任何其他 go-routine(没有这样的)从该通道读取相同的数据。

但输出显示:

$ bin/cs61a 
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /home/user/../myhub/cs61a/Main.go:8 +0x54
$
Run Code Online (Sandbox Code Playgroud)

编辑:

重点是:“主 goroutine 被阻塞,因此所有 goroutine 都被阻塞,因此这是一个死锁。” 在下面的代码中,非主协程也在通道上被阻塞,不是所有的协程都应该被阻塞吗?

package main

import (
    "fmt"
    "time"
)

func makeRandom(randoms chan int) {
    var ch chan int
    fmt.Printf("print 1\n")
    <-ch
    fmt.Printf("print 2\n")
}

func main() {

    randoms := make(chan int)

    go makeRandom(randoms)


}
Run Code Online (Sandbox Code Playgroud)

编辑2:

对于您在答案中的观点:“并非所有的 goroutine 都被阻塞,所以这不是死锁”。在下面的代码中,只有main()goroutine 被阻塞,但没有worker()

package main

import (
    "fmt"
)

func worker() {

    fmt.Printf("some work\n")

}

func main() {

    ch := make(chan int)

    go worker()
    <-ch

}
Run Code Online (Sandbox Code Playgroud)

并且输出说死锁:

$ bin/cs61a 
some work
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/user/code/src/github.com/myhub/cs61a/Main.go:18 +0x6f
$
Run Code Online (Sandbox Code Playgroud)

理想情况下main()不应该退出,因为通道资源被任何一个 go-routine 使用。

为什么通道上的 go-routine 块被认为是死锁?

icz*_*cza 8

在 Go 中,死锁是指所有现有的 goroutine 都被阻塞。

你的例子有一个 goroutine——主 goroutine——它被阻塞了,因此所有的 goroutines 都被阻塞了,因此它是一个死锁。

注意:由于所有的 goroutine 都被阻塞了,新的 goroutine 不会(不能)启动(因为它们只能通过运行的 goroutine 启动)。如果所有的 goroutine 都被阻塞了,不能做任何事情,那么永远等待是没有意义的。所以运行时退出。

编辑:

您在 main 中使用 sleep 的已编辑代码与此重复:Go channel deadlock is notposing。基本上,睡眠不是永远阻塞的操作(睡眠持续时间是有限的),因此在死锁检测中不考虑 goroutine 睡眠。

编辑#2:

从那以后你删除了sleep()但它没有改变任何东西。您有 2 个 goroutine:main和一个执行makeRandom(). makeRandom()被阻止并且main()不是。所以并不是所有的 goroutine 都被阻塞了,所以这不是死锁。

编辑#3:

在您的最后一个示例中,当运行时检测到死锁时,只有一个 goroutine 仍在运行:main(). 确实,您启动了一个 goroutine execution worker(),但它只打印文本并终止。“过去”的 goroutine 不算数,终止的 goroutine 也无法改变现有 goroutine 的阻塞状态。只有现有的 goroutine 才算数。