我有一个简单的并发用例在去,它让我疯狂我找不出一个优雅的解决方案。任何帮助,将不胜感激。惯用的goroutine终止和错误处理
我想写一个方法fetchAll
并行查询来自远程服务器的未指定数量的资源。如果任何提取失败,我想立即返回第一个错误。从阅读https://blog.golang.org/pipelines我可以创建一个信号通道清理其他线程https://play.golang.org/p/Be93J514R5
我知道:
我最初的,幼稚的做法,漏够程:
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())
}
游乐场。或者,我可以使用context
来完成它。但是,似乎这样一个简单的用例应该有一个我错过的更简单的解决方案。
'ec:= chan error(nil)'很有趣,我之前没有看到过这种模式。我认为'select'原因是以随机顺序执行的。在'ec <-err'之前发送'done <-true'是否有竞争条件? – gerad
很好,绝对有一场比赛!我写得很快,就像我提到的那样,没有测试它(你应该始终这样做)。幸运的是,修复这个错误只会让整个代码变得更简单,在这种情况下,不需要'chan error(nil)'技巧(当你想阻止select语句的发送时,这很有用,所以你不要不必写多个条件选择)。感谢您指出我的错误:) – Aedolon
这可以进一步简化。你不需要单独完成和错误的渠道,还有其他一些事情不会改进。 https://play.golang.org/p/1a0ZXuy3Dz –