【问题标题】:Channels and Wait Groups Entering Deadlock通道和等待组进入死锁
【发布时间】:2022-01-27 23:03:50
【问题描述】:

我在整理 go 例程并让它们与主要 go 例程上的频道进行通信时遇到问题。为简化起见,我的代码如下所示:


func main() {
    channel := make(chan string)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go performTest(channel, &wg, i)
    }

    wg.Wait()
    close(channel)

    for line := range channel {
        fmt.Print(line)
    }
}

func performTest(channel chan string, wg *sync.WaitGroup, i int) {
     defer wg.Done()
     // perform some work here
     result := fmt.sprintf("Pretend result %d", i)
     channel <- result
}

这似乎进入了某种僵局,但我不明白为什么。它卡在wg.Wait() 上,尽管我希望它会在所有 goroutine 都在等待组上调用 Done 后继续。我在这里想念什么?我想等待 goroutines,然后遍历通道中的所有结果。

【问题讨论】:

  • 你正在等待阅读频道 - go.dev/play/p/EnbfKe_d-BL
  • 干杯,把等待放在工作之后,但我不明白为什么我不能在阅读频道之前等待?
  • 问题是必须从不同的 goroutine 写入/读取无缓冲的 chanel。在您的示例中,如果您将通道缓冲为channel := make(chan string, 10),则不会发生死锁。更好的选择是将写入或读取部分运行到它自己的 goroutine 中。

标签: go channel goroutine


【解决方案1】:

您可以等待组并在单独的 go 例程中关闭频道。如果通道关闭,您在通道上的范围将在收到最后一个发送的值后结束。

如果您只是等待,则不会从频道收到任何内容。由于通道没有缓冲,performTest goroutines 将无法发送。对于无缓冲通道,发送操作将阻塞,直到它被接收。因此,延迟的wg.Done 调用将永远不会发生,您的程序将陷入僵局。因为Done 仅在执行永久阻塞发送之后才被调用。

func main() {
    channel := make(chan string)
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go performTest(channel, &wg, i)
    }
    
    // this is the trick
    go func() {
        wg.Wait()
        close(channel)
    }()

    for line := range channel {
        fmt.Print(line)
    }
}

func performTest(channel chan string, wg *sync.WaitGroup, i int) {
    defer wg.Done()
    // perform some work here
    result := fmt.Sprintf("Pretend result %d\n", i)
    channel <- result
}

https://play.golang.com/p/5pACJzwL4Hi

【讨论】:

  • 很好的答案。谢谢您的帮助。我是否正确地说,如果你不为通道提供长度,它一次只能缓冲一个值,所以就像你说的,在第一个写入通道之后,直到缓冲区不能再写入读完了吗?
  • 还有一个问题:如果运行 performTest 函数的最后一个 go 例程在等待组上调用 Done,那么是否存在竞争条件的风险,然后 main 中的匿名函数继续在主 go 例程中从通道缓冲区读取所有数据之前关闭通道?希望这是有道理的。
  • 你是对的,不是我说的。如果您缓冲,您仍然可以从通道中读取所有消息。只有在收到最后一条消息后,通道才会关闭。我把那段删掉了,因为它不正确。如果它表现得那样,那就没有多大意义了。
  • 所以你可以从一个封闭的频道阅读(我想你在说什么)?
  • 另外你不能在有接收者之前发送给它。好点,我会在我的回答中纠正它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 2019-07-20
  • 1970-01-01
  • 2015-09-20
  • 2011-08-24
相关资源
最近更新 更多