为什么这会导致Go陷入僵局?

Ell*_*nce 2 deadlock go

这不是关于如何更好地写这个的问题.这是一个特别关于为什么Go在这种情况下导致死锁的问题.

package main

import "fmt"

func main() {
    chan1 := make(chan bool)
    chan2 := make(chan bool)

    go func() {
        for {
            <-chan1
            fmt.Printf("chan1\n")
            chan2 <- true
        }
    }()

    go func() {
        for {
            <-chan2
            fmt.Printf("chan2\n")
            chan1 <- true
        }
    }()

    for {
        chan1 <- true
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

chan1
chan2
chan1
chan2
chan1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
goroutine 5 [chan send]:
goroutine 6 [chan send]:
exit status 2
Run Code Online (Sandbox Code Playgroud)

为什么这不会导致无限循环?为什么在放弃之前它会完成两次完整的"ping-ping"(而不仅仅是一次)?

tom*_*asz 7

从运行时的角度来看,你会遇到死锁,因为所有例程都试图发送到一个通道,并且没有例程等待接收任何东西.

为什么会这样呢?我会给你一个故事,因为我喜欢想象我遇到死锁时我的惯例在做什么.

你有两个球员(套路)和一个球(true值).每个玩家等待一个球,一旦他们得到它,他们会将其传递给另一个玩家(通过一个频道).这是你的两个例程真正做的事情,这确实会产生无限循环.

问题是你的主循环中引入的第三个玩家.他躲在第二个球员后面,一旦他看到第一个球员空手,他就会向他投掷另一个球.所以我们最终让两名球员拿球,不能将球传给另一名球员,因为另一名球员已经掌握了(第一个)球.隐藏的,邪恶的球员也试图通过另一个球.每个人都很困惑,因为有三个球,三个球员,没有空手.

换句话说,你已经介绍了第三个破坏游戏的玩家.他应该是一个仲裁者,在比赛开始的时候传递第一个球,看着它,但是停止生产球!这意味着,而不是在你的主程序中有一个循环,应该是简单的chan1 <- true(和一些条件等待,所以我们不退出程序).

如果在主例程循环中启用日志记录,您将看到在第三次迭代时始终发生死锁.执行其他例程的次数取决于调度程序.带回故事:第一次迭代是第一球的开球; 下一次迭代是一个神秘的第二球,但这可以处理.第三次迭代是一个僵局 - 它使第三个球无法被任何人处理.