英文:
How can I coalesce the results of two gorountines before returning?
问题
我在Go应用程序中有一个Web请求处理程序,需要向其他URL发出2个或更多的请求。我想收集每个URL的结果,将每个结果合并成一个JSON对象,并通过我的请求处理程序返回。这些请求不依赖于彼此,也不需要按顺序执行。
在Go中,最好的模式是什么?我应该使用通道和WaitGroup
吗?
英文:
I have a web request handler in a Go app that needs to make 2+ requests to other URLs. I'd like to collect the results from each URL, coalesce each result into a single JSON object, and return via my request handler. The requests are not dependent upon each other and do not need to be sequenced.
What's the best pattern for doing this in Go? Should I use a channel and a WaitGroup
?
答案1
得分: 1
对于简单的情况,我会使用一组本地变量和一些goroutine来设置这些变量,同时使用waitgroup来知道何时完成所有操作:
var a string
var b string
wg := sync.WaitGroup{}
wg.Add(2)
go func(){
time.Sleep(5 * time.Second) // 发起请求
a = "foo"
wg.Done()
}()
go func(){
time.Sleep(3 * time.Second) // 发起请求
b = "bar"
wg.Done()
}()
wg.Wait()
fmt.Println(a, b) // 结果合并
如果你想要更复杂的行为,比如超时或部分结果,那么你可能希望子请求在一个通道上回传结果,然后你可以在select语句中选择处理:
// 确保缓冲区足够大,以便垃圾回收可以在超时时清理
ch := make(chan string, 2)
go func() {
time.Sleep(5 * time.Second) // 发起请求
ch <- "foo"
}()
go func() {
time.Sleep(2 * time.Second) // 发起请求
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)
这样做不能完全保持顺序,但如果顺序很重要,你可以创建另一个通道。这是一般的思路。
英文:
For simple things I would use a set of local variables and some goroutines that set those variables, along with a waitgroup to know when everything is finished:
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
If you want more complicated behaviour like timeouts or partial results, then you probably want your subrequests to communicate results back on a channel you can select on:
// 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)
That doesn't fully preserve order, but you could make another channel if that is important. Thats the general idea anyway.
答案2
得分: 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>
英文:
I wrote this library that can help simplify running the go routines in parallel without having to worry about the low-level details https://github.com/shomali11/parallelizer
So in your case, you could do this:
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)
}
The output:
Done
Results 1: 1
Results 2: 2
Error: <nil>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论