等待多个goroutines的结果

I am searching a way to execute asynchronously two functions in go which returns different results and errors, wait for them to finish and print both results. Also if one of function returned error I do not want to wait for another function, and just print the error. For example, I have this functions:

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

Here https://play.golang.org/p/-8StYapmlg is how I implemented it, but it has too much code I think. It can be simplified by using interface{} but I don't want to go this way. I want something simpler as, for example, can be implemented in C# with async/await. Probably there is some library that simplifies such operation.

UPDATE: Thank for your responses! It is awesome how fast I got help! I like the usage of WaitGroup. It obviously makes the code more robust to changes, so I easily can add another async method without changing exact count of methods in the end. However, there is still so much code in comparison to same in C#. I know that in go I don't need to explicitly mark methods as async, making them actually to return tasks, but methods call looks much more simple, for example, consider this link actually catching exception is also needed By the way, I found that in my task I actually don't need to know returning type of the functions I want to run async because it will be anyway marshaled to json, and now I just call multiple services in the endpoint layer of go-kit.

dtmtu02882
dtmtu02882 如果一次执行中发生错误,则可能会关闭多个goroutine。
2 年多之前 回复

3个回答

You should create two channels for errors and results, then first read errors if no erorrs then read the results, this sample should works for your use case:

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, " ------------")
    }
}



我认为这里可以使用sync.WaitGroup。 它可以等待不同数量的动态goroutine。 </ p>
</ div>

展开原文

原文

I think here sync.WaitGroup can be used. It can waits for different and dynamic number of goroutines.

dongyan6503
dongyan6503 nathanleclaire.com/blog/2014/02/15/…关于sync.WaitGroup和文档golang.org/pkg/sync/#WaitGroup的不错的博客文章
2 年多之前 回复

I have created a smaller, self-contained example of how you can have two go routines run asynchronously and wait for both to finish or quit the program if an error occurs (see below for an explanation):

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

What I do is create a channel that is used for synchronization of the two go routines. Writing to and reading from it blocks. The channel is used to pass the error value around, or nil if the function succeeds.

At the end I read one value per async go routine from the channel. This blocks until a value is received. If an error occurs, I exit the loop, thus quitting the program.

The functions either succeed or fail randomly.

I hope this gets you going on how to coordinate go routines, if not, let me know in the comments.

Note that if you run this in the Go Playground, the rand.Seed will do nothing, the playground always has the same "random" numbers, so the behavior will not change.

dtnd30892
dtnd30892 实际上,在我的示例中,程序将终止main go例程,关闭运行时,因此不会泄漏任何内容。 但是您是对的,在更复杂的情况下,这可能是个问题。
2 年多之前 回复
doujiu8479
doujiu8479 关闭(或缓冲)该通道,否则将泄漏goroutine(两次发送,但如果第一个值不为nil,则只有一个接收;就像问题中的示例一样)。
2 年多之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问