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

我正在练习通过同时将计算分成 100 个组来计算阶乘的挑战,我在 WaitGroups 上解决了很多问题,但仍然在函数中calculateFactorial我在通道部分的范围上遇到了死锁。希望有人能指出这里的问题,谢谢。

更新:它通过简单地将 更改为缓冲通道解决了上述代码中的问题。

你永远不应该仅仅为了修复死锁而添加缓冲。如果您的程序死锁,从零缓冲开始并仔细考虑依赖关系,修复起来要容易得多。然后在您知道不会死锁时添加缓冲。

那么谁能帮我弄清楚如何不为此使用缓冲通道?是否可以?

此外,我对导致死锁的确切原因做了一些研究。

如果通道是无缓冲的,则发送方会阻塞,直到接收方收到该值。如果通道有缓冲区,发送方只会阻塞直到值被复制到缓冲区;如果缓冲区已满,这意味着等待某个接收者检索到一个值。

否则说:

  1. 当通道已满时,发送方等待另一个 goroutine 通过接收来腾出一些空间

  2. 你可以看到一个无缓冲的通道总是满的:必须有另一个 goroutine 来接收发送者发送的内容。

所以在我原来的情况下,可能导致僵局的原因可能是:

  1. 频道上的范围没有接收到?

  2. 通道上的范围未在单独的 go 例程中接收。?

  3. 没有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 不能同时做两件事:它不能发送和接收;它必须是一个或另一个。



蝴蝶不菲
浏览 115回答 2
2回答

慕哥9229398

死锁不在 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

梵蒂冈之花

您需要为变量添加一个缓冲区oneResult
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go