如何在返回之前合并两个gorountines的结果?

时间:2015-08-03 18:54:08

标签: ajax http go goroutine

我在Go应用中有一个Web请求处理程序,需要向其他URL发出2+个请求。我想从每个URL收集结果,将每个结果合并到一个JSON对象中,并通过我的请求处理程序返回。请求不依赖于彼此,也不需要排序。

在Go中执行此操作的最佳模式是什么?我应该使用频道和WaitGroup吗?

2 个答案:

答案 0 :(得分:1)

对于简单的事情,我会使用一组局部变量和一些设置这些变量的goroutine,以及一个等待组来知道什么时候完成:

    var a string
    var b string
    wg := sync.WaitGroup{}
    wg.Add(2)
    go func(){
        time.Sleep(5 * time.Second) // make a request
        a = "foo"
        wg.Done()
    }()
    go func(){
        time.Sleep(3 * time.Second) // make a request
        b = "bar"
        wg.Done()
    }()
    wg.Wait()
    fmt.Println(a,b) //combine results

playground link

如果您想要更复杂的行为,例如超时或部分结果,那么您可能希望子请求在您可以选择的频道上传回结果:

// make sure to buffer to max number of senders so garbage collection can clean up
// if we time out
ch := make(chan string, 2)
go func() {
    time.Sleep(5 * time.Second) // make a request
    ch <- "foo"
}()
go func() {
    time.Sleep(2 * time.Second) // make a request
    ch <- "bar"
}()
results := []string{}
timeout := time.After(4 * time.Second)
Loop:
for {
    select {
    case r := <-ch:
        results = append(results, r)
        if len(results) == 2 {
            break Loop
        }
    case <-timeout:
        break Loop
    }
}
fmt.Println(results)

playground Link

这并不能完全保留秩序,但如果这很重要,你可以制作另一个频道。无论如何,这是一般的想法。

答案 1 :(得分:0)

我编写了这个库,可以帮助简化并行运行go例程,而不必担心低级细节https://github.com/shomali11/parallelizer

所以在你的情况下,你可以这样做:

package main

import (
    "github.com/shomali11/parallelizer"
    "fmt"
)

func main() {
    group := parallelizer.DefaultGroup()

    result1 := &SomeResultStructure{}
    group.Add(func(result *SomeResultStructure) {
        return func () {
            ...
            result.SomeValue = "1"
        }
    }(result1))

    result2 := &SomeResultStructure{}
    group.Add(func(result *SomeResultStructure) {
        return func () {
            ...
            result.SomeValue = "2"
        }
    }(result2))

    err := group.Run()

    fmt.Println("Done")
    fmt.Println(fmt.Sprintf("Results 1: %v", result1.SomeValue))
    fmt.Println(fmt.Sprintf("Results 2: %v", result2.SomeValue))
    fmt.Printf("Error: %v", err)
}

输出:

Done
Results 1: 1
Results 2: 2
Error: <nil>