如何等待多个 goroutine 完成?

Posted

技术标签:

【中文标题】如何等待多个 goroutine 完成?【英文标题】:How can wait for more than one goroutine to finish? 【发布时间】:2018-02-24 09:09:57 【问题描述】:
package main

var fooRunning = false
var barRunning = false

func foo() 
    fooRunning = true
    defer func()  fooRunning = false ()
    if barRunning 
        // wait for bar() to finish
    
    ...


func bar() 
    barRunning = true
    defer func()  barRunning = false ()
    if fooRunning 
        // wait for foo() to finish
    
    ...

在我的情况下,如果我们运行go foo(),它应该等待bar() 完成,反之亦然。最好的方法是什么?请注意,它们也可以独立执行。

【问题讨论】:

相关/可能与What's wrong with this golang code?重复 这些要求导致必须正确处理大量竞争条件(如果其他例程在您的正检查后立即结束怎么办?如果其他人在负检查后立即开始另一个例程等怎么办? . 如果有人运行 foo() 的 2 个例程怎么办?我强烈建议您创建一个更好的设计,让您没有 2 个相互依赖于另一个独立运行状态的 goroutine。 问题中的代码具有@nos 指出的设计死锁。没有适用于给定场景的解决方案。 【参考方案1】:

任何具体的设计都无法安全地满足您的要求。按照规定,您说foobar 可以 在并发 goroutine 中运行,并且如果其中一个或两个都已启动,则另一个应等待它们都完成。但是,这太弱了。如果foo 启动然后结束,但bar 还没有开始运行,会发生什么?如果bar 根本不运行怎么办?或者如果bar 运行,但foo 从来没有运行呢?

您是否要求foobar 都必须启动和完成才能使您的程序正确?如果是这样,我可以猜到你的意思是:你想要一个 barrier 等待他们都完成后再继续。

package main

import (
    "fmt"
    "sync"
    "time"
)

func foo() 
    fmt.Println("foo")


func bar() 
    fmt.Println("bar")


func within(wg *sync.WaitGroup, f func()) 
    wg.Add(1)
    go func() 
        defer wg.Done()
        f()
    ()


func main() 
    var wg sync.WaitGroup
    within(&wg, foo)
    within(&wg, bar)
    wg.Wait()
    fmt.Println("Both foo and bar completed.")

(That same example in the Playground)

注意这里,foobar 都不是相互感知的;为了协调这两个呼叫,只有他们的呼叫者是。

您最初的尝试可能会导致您走上使foobar 各自关闭或接受sync.WaitGroup 作为参数的道路,每个函数在退出之前首先是adding itself to the group 和waiting on it。那就是疯狂。

如果foobar 有机会将自身添加到WaitGroup 之前启动并完成,则foo 将在bar 之前退出,即使您可以声称它们已经同时运行,或者反过来在foo 之前运行bar 可以注册其活动状态。同样,由于这是您程序的一个未明确说明的方面,我建议您转而关注更高级别的障碍,而不是这两个函数的相互依赖。

【讨论】:

我们应该总是为sync.WaitGroup传递指针。游乐场也有同样的建议。 谢谢,@SouravPrem。我根据您的建议更新了此处的文本和 Playground 链接。【参考方案2】:

您可以使用频道!正如我从生锈的过程中所记得的那样,这会给:

func foo() 
    c := make(chan int)
    go bar(c)
    <-c

在酒吧里

func bar(c chan int) 
    // do stuff here
    c <- 0

【讨论】:

如果 bar() 没有运行怎么办? Channel 会阻止进一步的执行,对吧? 是的,除非另一个 goroutine 在通道中推送一个整数,否则&lt;-c 将阻塞,直到给出更多整数。如果您忘记在bar 中为通道提供整数,那么您的程序将陷入死锁 这仅提供单向阻塞,其中问题需要双向;它没有解决问题中设计的僵局;并且它不必要地使用了渠道。 sync.WaitGroup 正是为这个用例而构建的,但在从设计中消除死锁之前无法提出解决方案。

以上是关于如何等待多个 goroutine 完成?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 time.Sleep 的情况下等待所有 goroutines 完成?

等待 goroutine 完成的正确方法

第三章 Goroutine调度策略(16)

如何关闭多个 goroutine 正在发送的通道?

go 等待所有 goroutine 执行结束的方法

Go sync.WaitGroup 等待Goroutine执行完成