【问题标题】:Wait result of multiple goroutines等待多个 goroutine 的结果
【发布时间】:2018-04-05 00:31:48
【问题描述】:

我正在寻找一种在 go 中异步执行两个函数的方法,它们返回不同的结果和错误,等待它们完成并打印两个结果。此外,如果其中一个函数返回错误,我不想等待另一个函数,而只是打印错误。 例如,我有这个功能:

func methodInt(error bool) (int, error) {
    <-time.NewTimer(time.Millisecond * 100).C
    if error {
        return 0, errors.New("Some error")
    } else {
        return 1, nil
    }
}

func methodString(error bool) (string, error) {
    <-time.NewTimer(time.Millisecond * 120).C
    if error {
        return "", errors.New("Some error")
    } else {
        return "Some result", nil
    }
}

这里https://play.golang.org/p/-8StYapmlg 是我实现它的方式,但我认为它的代码太多了。它可以通过使用 interface{} 来简化,但我不想这样。我想要一些更简单的东西,例如,可以在 C# 中使用 async/await 实现。可能有一些库可以简化这种操作。

更新:感谢您的回复!我得到帮助的速度真是太棒了!我喜欢 WaitGroup 的用法。它显然使代码对更改更加健壮,因此我可以轻松地添加另一个异步方法,而最终无需更改方法的确切计数。但是,与 C# 中的相同代码相比,仍然有很多代码。我知道在 go 中我不需要将方法显式标记为异步,使它们实际上返回任务,但是方法调用看起来要简单得多,例如,考虑这个链接actually catching exception is also needed 顺便说一句,我发现在我的任务中我实际上不需要知道我想要运行异步的函数的返回类型,因为它无论如何都会被编组为 json,现在我只是在 go 的端点层调用多个服务-工具包。

【问题讨论】:

标签: go channel goroutine


【解决方案1】:

您应该为错误和结果创建两个通道,然后如果没有错误则首先读取错误然后读取结果,此示例应该适用于您的用例:

package main

import (
    "errors"
    "sync"
)

func test(i int) (int, error) {
    if i > 2 {
        return 0, errors.New("test error")
    }
    return i + 5, nil
}

func test2(i int) (int, error) {
    if i > 3 {
        return 0, errors.New("test2 error")
    }
    return i + 7, nil
}

func main() {
    results := make(chan int, 2)
    errors := make(chan error, 2)
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        result, err := test(3)
        if err != nil {
            errors <- err
            return
        }
        results <- result
    }()
    wg.Add(1)
    go func() {
        defer wg.Done()
        result, err := test2(3)
        if err != nil {
            errors <- err
            return
        }
        results <- result
    }()

    // here we wait in other goroutine to all jobs done and close the channels
    go func() {
        wg.Wait()
        close(results)
        close(errors)
    }()
    for err := range errors {
        // here error happend u could exit your caller function
        println(err.Error())
        return

    }
    for res := range results {
        println("--------- ", res, " ------------")
    }
}

【讨论】:

    【解决方案2】:

    我觉得这里可以使用sync.WaitGroup。它可以等待不同和动态数量的 goroutine。

    【讨论】:

    【解决方案3】:

    我创建了一个较小的、独立的示例,说明如何让两个 go 例程异步运行并等待两者完成或在发生错误时退出程序(请参阅下面的说明):

    package main
    
    import (
        "errors"
        "fmt"
        "math/rand"
        "time"
    )
    
    func main() {
        rand.Seed(time.Now().UnixNano())
    
        // buffer the channel so the async go routines can exit right after sending
        // their error
        status := make(chan error, 2)
    
        go func(c chan<- error) {
            if rand.Intn(2) == 0 {
                c <- errors.New("func 1 error")
            } else {
                fmt.Println("func 1 done")
                c <- nil
            }
        }(status)
    
        go func(c chan<- error) {
            if rand.Intn(2) == 0 {
                c <- errors.New("func 2 error")
            } else {
                fmt.Println("func 2 done")
                c <- nil
            }
        }(status)
    
        for i := 0; i < 2; i++ {
            if err := <-status; err != nil {
                fmt.Println("error encountered:", err)
                break
            }
        }
    }
    

    我所做的是创建一个用于同步两个 go 例程的通道。写入和读取它会阻塞。通道用于传递错误值,如果函数成功,则为 nil。

    最后,我从通道中为每个异步 go 例程读取一个值。这会阻塞,直到收到一个值。如果发生错误,我退出循环,从而退出程序。

    函数要么成功要么随机失败。

    我希望这能让你了解如何协调 go 例程,如果没有,请在 cmets 中告诉我。

    注意,如果你在 Go Playground 中运行它,rand.Seed 将什么都不做,playground 总是有相同的“随机”数字,所以行为不会改变。

    【讨论】:

    • 关闭(或缓冲)通道,否则会泄漏 goroutines(两个发送,但如果第一个值不是 nil 则只有一个接收;就像问题中的示例一样)。
    • 实际上,在我的示例中,程序将终止主 go 例程,关闭运行时,因此没有任何泄漏。但你是对的,在更复杂的情况下,这可能是个问题。
    猜你喜欢
    • 2018-02-24
    • 2017-08-14
    • 2019-07-18
    • 2023-03-10
    • 2014-05-11
    • 2016-11-13
    • 2020-06-18
    • 2021-03-17
    • 2019-10-07
    相关资源
    最近更新 更多