等待多个goroutines的结果

时间:2017-10-24 09:17:58

标签: go channel goroutine

我正在寻找一种方法,在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是我实现它的方式,但我认为它有太多代码。它可以通过使用界面{}来简化,但我不想这样做。我想要更简单的东西,例如,可以使用async / await在C#中实现。可能有一些库可以简化这种操作。

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

3 个答案:

答案 0 :(得分:4)

我认为这里可以使用sync.WaitGroup。它可以等待不同的动态数量的goroutines。

答案 1 :(得分:3)

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

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 :(得分:0)

我已经创建了一个较小的,自包含的示例,说明如何异步运行两个例程,并在发生错误时等待两者完成或退出程序(参见下面的解释):

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
        }
    }
}

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

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

这些功能随机成功或失败。

我希望这能帮助您了解如何协调常规,如果没有,请在评论中告诉我。

注意如果你在Go Playground中运行它,那么rand.Seed什么都不做,操场总是有相同的&#34;随机&#34;数字,所以行为不会改变。