“所有 goroutine 都在睡觉 - 死锁!”的情况我不明白为什么

一个典型的案例all goroutines are asleep, deadlock!,但无法弄清楚

我正在解析维基词典 XML 转储来构建单词数据库。我将每篇文章的文本的解析推迟到一个 Goroutine,希望它能加快这个过程。
它有 7GB,在我的机器上串行执行时,处理时间不到 2 分钟,但如果我可以利用所有内核,为什么不呢。

一般来说,我是线程新手,遇到错误all goroutines are asleep, deadlock!
这是怎么回事?

这可能根本没有性能,因为它使用无缓冲的通道,因此所有 goroutine 实际上最终都会串行执行,但我的想法是学习和理解线程,并衡量不同替代方案所需的时间:

  • 无缓冲通道

  • 不同大小的缓冲通道

  • 一次只调用尽可能多的 goroutineruntime.NumCPU()

我的伪代码代码摘要:


慕的地10843
浏览 100回答 3
3回答

三国纷争

while tag := xml.getNextTag() {&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; go parseTagText(chan, wg, tag.text)&nbsp; &nbsp; // consume a channel message if available&nbsp; &nbsp; select {&nbsp; &nbsp; case msg := <-chan:&nbsp; &nbsp; &nbsp; &nbsp; // do something with msg&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; default:&nbsp; &nbsp; }}// reading tags finished, wait for running goroutines, consume what's left on the channelfor msg := range chan {&nbsp; &nbsp; // do something with msg}// Sometimes this point is never reached, I get a deadlockwg.Wait()----func parseTagText(chan, wg, tag.text) {&nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; // parse tag.text&nbsp; &nbsp; chan <- whatever // just inform that the text has been parsed}完整代码: https://play.golang.org/p/0t2EqptJBXE

慕莱坞森

在 Go Playground 上的完整示例中,您:创建一个通道(第 39 行,results := make(chan langs))和一个等待组(第 40 行,var wait sync.WaitGroup)。到目前为止,一切都很好。循环:在循环中,有时会衍生出一个任务:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;...various&nbsp;conditions...&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wait.Add(1)&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;go&nbsp;parseTerm(results,&nbsp;&wait,&nbsp;text) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}在循环中,有时会从通道进行非阻塞读取(如您的问题所示)。这里也没有问题。但...在循环结束时,使用:for&nbsp;res&nbsp;:=&nbsp;range&nbsp;results&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;... }在所有作家完成后,无需精确地调用close(results)一个地方。此循环使用从通道的阻塞读取。只要某个 writer goroutine 仍在运行,阻塞读取就可以阻塞,而不会导致整个系统停止,但是当最后一个 writer 完成写入并退出时,就没有剩余的 writer goroutine 了。任何其他剩余的 goroutine 可能会拯救你,但没有。由于您使用var wait正确(在正确的位置添加 1,并Done()在 writer 中的正确位置调用),解决方案是再添加一个 goroutine,它将拯救您:go&nbsp;func()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;wait.Wait() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;close(results) }()您应该在进入循环之前关闭这个救援 goroutine&nbsp;for res := range results。(如果您更早地将其分离,它可能会wait很快看到变量计数减至零,就在它通过分离另一个 再次计数之前parseTerm。)这个匿名函数将阻塞在wait变量的Wait()函数中,直到最后一个 writer Goroutine 调用了 Final&nbsp;wait.Done(),这将解除对这个Goroutine 的阻塞。然后这个 goroutine 将调用close(results),这将安排goroutinefor中的循环main完成,从而解锁该 goroutine。当这个 goroutine(救援者)返回并因此终止时,不再有救援者,但我们不再需要任何救援者。(这个主代码然后wait.Wait()不必要地调用:因为直到新的goroutine中的已经解除阻塞for才终止,我们知道下一个将立即返回。所以我们可以放弃第二个调用,尽管保留它是无害的。)wait.Wait()wait.Wait()

慕仙森

问题是没有任何东西可以关闭结果通道,但范围循环仅在关闭时退出。我简化了您的代码来说明这一点并提出了一个解决方案 - 基本上使用 goroutine 中的数据:// This is our producerfunc foo(i int, ch chan int, wg *sync.WaitGroup) {&nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; ch <- i&nbsp; &nbsp; fmt.Println(i, "done")}// This is our consumer - it uses a different WG to signal it's donefunc consumeData(ch chan int, wg *sync.WaitGroup) {&nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; for x := range ch {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(x)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("ALL DONE")}func main() {&nbsp; &nbsp; ch := make(chan int)&nbsp; &nbsp; wg := sync.WaitGroup{}&nbsp; &nbsp; // create the producers&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go foo(i, ch, &wg)&nbsp; &nbsp; }&nbsp; &nbsp; // create the consumer on a different goroutine, and sync using another WG&nbsp; &nbsp; consumeWg := sync.WaitGroup{}&nbsp; &nbsp; consumeWg.Add(1)&nbsp; &nbsp; go consumeData(ch,&consumeWg)&nbsp; &nbsp; wg.Wait()&nbsp; // <<<< means that the producers are done&nbsp; &nbsp; close(ch) // << Signal the consumer to exit&nbsp; &nbsp; consumeWg.Wait() // << Wait for the consumer to exit}
打开App,查看更多内容
随时随地看视频慕课网APP