【问题标题】:How to finish all goroutines after one is done一个完成后如何完成所有的goroutine
【发布时间】:2023-04-03 00:40:01
【问题描述】:

我想运行多个goroutine,进行一些处理,将结果放入channel,当至少有一个goroutine完成时,完成所有其他goroutine并从channel返回结果。

所以,我尝试使用等待组来实现这一点,但似乎我没有正确使用等待组。

    package optimizer

    import (
        "github.com/tevino/abool"
        "errors"
        "sync"
        "runtime"
        "log"
    )

    type Optimizer struct {
        Handlers      []ofdHandler.Handler
    }

    func Make(handlers []ofdHandler.Handler, maxProcs int) Optimizer {
        runtime.GOMAXPROCS(maxProcs)
        return Optimizer{Handlers: handlers}
    }

    func (o Optimizer) Optimize(params operations.GetV1ReceiptsParams) (*models.Receipt, error) {
        var wg sync.WaitGroup
        wg.Add(len(o.Handlers))

        results := make(chan *models.Receipt)
        isCalculated := abool.NewBool(false)

        for _, handler := range o.Handlers {
            go func(handler ofdHandler.Handler) {
                log.Println("Starting handler: ", handler.GetName())
                defer wg.Done()

                if isCalculated.IsSet() {
                    log.Println("Result is calculated, exiting goroutine...")
                    return
                }

                receipt, err := handler.Handle(params)
                if err != nil {
                    log.Println(err)
                    return
                }

                if isCalculated.IsSet() {
                    log.Println("Result is calculated, exiting goroutine...")
                    return
                }

                log.Println("Writing result to channel...")
                isCalculated.Set()
                results <- receipt
            }(handler)
        }

        log.Println("Waiting...")
        wg.Wait()

        if receipt, ok := <-results; ok {
            return receipt, nil
        }

        return nil, errors.New("couldn't optimize with current list of Handlers")
    }

【问题讨论】:

    标签: go


    【解决方案1】:

    最好的方法是使用context。在Go blog 上有一个很好的示例,说明如何针对您的问题类型执行此操作。

    【讨论】:

    • 谢谢,会仔细看看。
    • 您对this 的博文有何看法?扇出,扇入
    【解决方案2】:

    您发布的代码不能按原样编译,您也没有描述问题(发生了什么而不是预期结果),但是通过查看代码我有两个建议:

    使结果通道缓冲,长度为1:

    results := make(chan *models.Receipt, 1)
    

    这允许首先完成的 goroutine 将结果写入通道并退出,从而减少等待组的计数器。

    最后,你应该使用SetToIf 而不是isCalculated.Set(),这样你就不会设置标志/结果两次(因此停滞不前,因为读者仍在等待等待组“完成”等等第二个 goroutine 将无法写入通道,这意味着等待组永远不会达到零):

    log.Println("Writing result to channel...")
    if isCalculated.SetToIf(false, true) {
       results <- receipt
    }
    

    【讨论】:

    • 谢谢@ain,成功了!顺便说一句,我应该在读取单个值后关闭通道吗?
    • 无需关闭通道,因为您不对其进行迭代,GC 也不关心通道是否关闭。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 2018-02-24
    • 2018-04-24
    • 2019-02-01
    • 2022-01-07
    • 2014-03-16
    • 1970-01-01
    相关资源
    最近更新 更多