我在 go 中有一个简单的并发用例,但我想不出一个优雅的解决方案来解决我的问题。
我想编写一个方法 fetchAll
从远程服务器并行查询未指定数量的资源。如果任何提取失败,我想立即返回第一个错误。
我最初的实现泄漏了 goroutines:
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
func fetchAll() error {
wg := sync.WaitGroup{}
errs := make(chan error)
leaks := make(map[int]struct{})
defer fmt.Println("these goroutines leaked:", leaks)
// run all the http requests in parallel
for i := 0; i < 4; i++ {
leaks[i] = struct{}{}
wg.Add(1)
go func(i int) {
defer wg.Done()
defer delete(leaks, i)
// pretend this does an http request and returns an error
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
errs <- fmt.Errorf("goroutine %d's error returned", i)
}(i)
}
// wait until all the fetches are done and close the error
// channel so the loop below terminates
go func() {
wg.Wait()
close(errs)
}()
// return the first error
for err := range errs {
if err != nil {
return err
}
}
return nil
}
func main() {
fmt.Println(fetchAll())
}
游乐场:https: //play.golang.org/p/Be93J514R5
我通过阅读 https://blog.golang.org/pipelines 知道我可以创建一个信号通道来清理其他线程。或者,我可能会使用 context
来完成它。但似乎这样一个简单的用例应该有一个我所缺少的更简单的解决方案。
原文由 gerad 发布,翻译遵循 CC BY-SA 4.0 许可协议
使用 错误组 使这更简单。这会自动等待所有提供的 Go Routines 成功完成,或者在任何一个例程返回错误的情况下取消所有剩余的例程(在这种情况下,该错误是返回给调用者的一个气泡)。