为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
哆啦的时光机 2021-11-22 17:01:52
这不是关于如何更好地写这个的问题。这是一个专门关于为什么 Go 在这种情况下导致僵局的问题。package mainimport "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    }}输出:chan1chan2chan1chan2chan1fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:goroutine 5 [chan send]:goroutine 6 [chan send]:exit status 2为什么这不会导致无限循环?怎么会在放弃之前进行两次完整的“ping-ping”(而不是一次)?
查看完整描述

3 回答

?
浮云间

TA贡献1829条经验 获得超4个赞

从运行时的角度来看,您会遇到死锁,因为所有例程都尝试发送到通道上,并且没有例程等待接收任何内容。

为什么会这样呢?我会给你一个故事,因为我喜欢想象当我遇到僵局时我的例程在做什么。

你有两名球员(例程)和一个球(true价值)。每个球员都在等待一个球,一旦他们拿到球,他们就会将球传回给另一个球员(通过通道)。这就是您的两个例程真正在做的事情,这确实会产生无限循环。

问题是在主循环中引入的第三个玩家。他躲在第二名球员身后,一旦他看到第一名球员空手而归,他就会向他扔另一个球。所以我们最终两个球员都拿着一个球,不能将球传给另一个球员,因为另一个球员已经(第一个)球在他的手中。隐藏的邪恶球员还试图再传一个球。每个人都很困惑,因为有三个球,三个球员,没有空手。

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

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


查看完整回答
反对 回复 2021-11-22
?
偶然的你

TA贡献1841条经验 获得超3个赞

看起来很复杂,但答案很简单。

在以下情况下会死锁:

  • 第一个例程试图写入 chan2

  • 第二条路线是尝试写入chan1.

  • Main 正在尝试写入chan1.

怎么会这样?例子:

  • 主要写道chan1。阻止另一次写入。

  • 例程 1:chan1从 Main 接收。印刷。写入时阻塞chan2

  • 例程2:chan2接收。印刷。写入时阻塞chan1

  • 例程 1:chan1从例程 2 接收。打印。写入时阻塞chan2

  • 例程2:chan2接收。印刷。写入时阻塞chan1

  • 主要写道chan1。阻止另一次写入。

  • 例程 1:chan1从 Main 接收。印刷。写入时阻塞chan2

  • 主要写道chan1。阻止另一次写入。

目前所有的例程都被阻止了。IE:

例程 1 无法写入,chan2因为例程 2 未接收但实际上在尝试写入时被阻止chan1。但是没有人在听chan1

正如@HectorJ 所说,这完全取决于调度程序。但在这种设置中,死锁是不可避免的。


查看完整回答
反对 回复 2021-11-22
?
忽然笑

TA贡献1806条经验 获得超5个赞

goroutine 1 [chan send]:

goroutine 5 [chan send]:

goroutine 6 [chan send]:

这说明了一切:你所有的 goroutine 都被阻塞,试图在一个通道上发送,而另一端没有人接收。


所以你的第一个 goroutine 阻塞了 chan2 <- true,你的第二个阻塞了chan1 <- true,你的主 goroutine 阻塞了它自己chan1 <- true。


至于为什么像您说的那样执行两次“完整的 ping-ping”,这取决于调度以及发送方<-chan1决定首先接收的信息。


在我的电脑上,我得到了更多,每次运行它都会有所不同:


chan1

chan2

chan1

chan2

chan1

chan2

chan1

chan2

chan1

chan2

chan1

chan2

chan1

fatal error: all goroutines are asleep - deadlock!


查看完整回答
反对 回复 2021-11-22
  • 3 回答
  • 0 关注
  • 158 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信