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

具有多个等待组的管道中通道范围内的死锁

具有多个等待组的管道中通道范围内的死锁

Go
蝴蝶不菲 2023-06-12 14:37:44
我正在练习通过同时将计算分成 100 个组来计算阶乘的挑战,我在 WaitGroups 上解决了很多问题,但仍然在函数中calculateFactorial我在通道部分的范围上遇到了死锁。希望有人能指出这里的问题,谢谢。更新:它通过简单地将 更改为缓冲通道解决了上述代码中的问题。你永远不应该仅仅为了修复死锁而添加缓冲。如果您的程序死锁,从零缓冲开始并仔细考虑依赖关系,修复起来要容易得多。然后在您知道不会死锁时添加缓冲。那么谁能帮我弄清楚如何不为此使用缓冲通道?是否可以?此外,我对导致死锁的确切原因做了一些研究。如果通道是无缓冲的,则发送方会阻塞,直到接收方收到该值。如果通道有缓冲区,发送方只会阻塞直到值被复制到缓冲区;如果缓冲区已满,这意味着等待某个接收者检索到一个值。否则说:当通道已满时,发送方等待另一个 goroutine 通过接收来腾出一些空间你可以看到一个无缓冲的通道总是满的:必须有另一个 goroutine 来接收发送者发送的内容。所以在我原来的情况下,可能导致僵局的原因可能是:频道上的范围没有接收到?通道上的范围未在单独的 go 例程中接收。?没有oneResult正确关闭,所以 range over channel 不知道尽头在哪里?对于数字 3,我不知道关闭 before range over 是否有任何错误oneResult,因为这种模式出现在互联网上的许多示例中。如果是 3 号,是不是等待组有问题?我得到了另一篇与我的情况非常相似的文章,他使用for { select {} }无限循环作为 range over 的替代方法,似乎解决了他的问题。go func() {        for{            select {            case p := <-pch:                findcp(p)            }        }    }()第 2 课 &mdash; 无缓冲通道无法保存值(是的,它就在名称&ldquo;无缓冲&rdquo;中),因此无论发送到该通道的什么,都必须立即由其他代码接收。接收代码必须在不同的 goroutine 中,因为一个 goroutine 不能同时做两件事:它不能发送和接收;它必须是一个或另一个。
查看完整描述

2 回答

?
慕哥9229398

TA贡献1877条经验 获得超6个赞

死锁不在 range-over-channel 循环上。如果您在操场上运行代码,您会在堆栈跟踪的顶部看到错误是由wg2.Wait(操场上的第 88 行并由堆栈跟踪指向)引起的。同样在堆栈跟踪中,您可以看到所有因死锁而未完成的 goroutine,这是因为oneResult<-t从未完成,因此循环中启动的 goroutine 都没有完成。

所以主要问题在这里:

wg2.Wait()

close(oneResult)


// ...


for n := range oneResult{

// ...

我想,在封闭的频道上循环也不是你想要的。但是,即使您没有关闭频道,该循环也永远不会开始,因为wg2.Wait()它会等到完成

oneResult <- t
wg2.Done()

但它永远不会完成,因为它依赖于已经运行的循环。该线路oneResult <- t不会完成,除非另一侧有人从该通道接收信号,这是您的循环,但是该通道范围循环仍在等待完成wg2.Wait()

所以本质上你在通道的发送者和接收者之间有一个“循环依赖”。

要解决此问题,您需要允许循环开始从通道接收数据,同时仍确保该通道在完成后关闭。你可以通过将两条等待和关闭线包装到它们自己的 goroutine 中来做这件事。

https://play.golang.com/p/rwwCFVszZ6Q


查看完整回答
反对 回复 2023-06-12
?
梵蒂冈之花

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

您需要为变量添加一个缓冲区oneResult

//img4.sycdn.imooc.com/6486bd790001948211160558.jpg


查看完整回答
反对 回复 2023-06-12
  • 2 回答
  • 0 关注
  • 114 浏览
慕课专栏
更多

添加回答

举报

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