Closed utterances-bot closed 2 years ago
大哥,我有这样的问题,errgroup可以做到当一个goroutine出错时返回错误,但我还希望每个goroutine能够返回额外的数据,比如一个res 结构体,这个该如何做啊
大哥,我有这样的问题,errgroup可以做到当一个goroutine出错时返回错误,但我还希望每个goroutine能够返回额外的数据,比如一个res 结构体,这个该如何做啊
每个 goroutine 都想要要返回数据的话,errgroup 就不太合适了,可以在 errgroup 的基础上自己封装一下,这个也不麻烦
大哥,我有这样的问题,errgroup可以做到当一个goroutine出错时返回错误,但我还希望每个goroutine能够返回额外的数据,比如一个res 结构体,这个该如何做啊
每个 goroutine 都想要要返回数据的话,errgroup 就不太合适了,可以在 errgroup 的基础上自己封装一下,这个也不麻烦
我这几天又研究了下,我在errgroup.Go的匿名函数里,调用一个耗时的方法,并最终返回 err。但这种情况就用不了<-ctx.Done() 来控制:当一个goroutine出错时返回错误,其他goroutine不再执行,因为这些所谓的其他goroutine正在执行这个耗时的方法
// errgroup:用于在goroutine并发过程失败时,终止其他goroutine,并返回第一个error
group := new(errgroup.Group)
// resp append 锁
var mutex sync.Mutex
// TODO: 控制数量
for i := 0; i < len(req.ins); i++ {
index := i
spec := req.ins[i]
param1:= req.param1
param2:= req.param2
group.Go(func() error {
// innerfunc十分耗时
ins, err := innerfunc(param0, param1, param2, index)
if err == nil {
mutex.Lock()
resp.ins= append(resp.ins, &ins)
mutex.Unlock()
}
return err
})
}
if err := group.Wait(); err != nil {
...
}
就像这个文章说的一样:https://www.5axxw.com/questions/content/xmjl08 我要如何做到当一个innerfunc出错时,其他goroutine的innerfunc可以中断返回,还是说没法做到
@slzheng2017 可以这么做, 当然实际上那个耗时的协程在没执行完的时候还是没有退出的,但是我猜测这样应该可以满足你的需求
func main() {
g, ctx := errgroup.WithContext(context.Background())
g.Go(func() error {
result := make(chan string)
go func() {
fmt.Println("will sleep 3s")
time.Sleep(3 * time.Second)
fmt.Println("sleep 3s done")
result <- "done"
}()
select {
case <-ctx.Done():
return fmt.Errorf("errgroup will exit")
case r := <-result:
fmt.Printf("res: %s\n", r)
return nil
}
})
g.Go(func() error {
fmt.Println("will sleep 1s")
time.Sleep(1 * time.Second)
// 返回错误一秒后主程序就会推出
return fmt.Errorf("err")
// 不返回错误,另外一个 协程会执行完毕
// return nil
})
fmt.Printf("done: %v", g.Wait())
}
这个其实涉及到了context的用法,可以参考这篇文章 https://lailin.xyz/post/go-training-week3-context.html#%E8%B6%85%E6%97%B6%E6%8E%A7%E5%88%B6
@slzheng2017 可以这么做, 当然实际上那个耗时的协程在没执行完的时候还是没有退出的,但是我猜测这样应该可以满足你的需求这个思路我也想到过了,但不要创建一个你不知道何时退出的 goroutine
这个思路我也想到过,但之前有看过你的文章:不要创建一个你不知道何时退出的 goroutine。所以就比较犹豫。一旦接收到ctx.Done()返回的信号,当前协程会退出,而那个耗时的协程就变成孤魂野鬼了,游荡中。。。
@slzheng2017 这个你要看具体的场景,目前看起来你这个只是在错误的情况下才会有不受控的 goroutine 并且这个地方看起来是一个请求,你可以给这个 goroutine 做好 recover,避免函数内 panic 导致整个服务挂掉,然后如果是请求的话设置好超时时间就行了
当然如果你这个经常出错,然后这个请求还会一直 hang 住另说
去看标准库的话你会发现一些类似的做法
感谢大神!
感谢大神!
客气了😝
大神,我又来了。我发现一个问题,使用errgroup+context后,性能有明显的下降。 我感觉通过管道来传递信号,性能影响很大 不使用errgroup和context:cost total time: 2925 ms 使用errgroup和context:cost total time: 4945 ms
@slzheng2017 这个要看你怎么用,channel 肯定比直接用锁性能低一些,它的实现就是用锁实现的,不过一般如果写业务开发,这点区别不是很大,优化这个还不如优化一下 sql 之类的见效快
最后的案例中,g3里需要return ctx.Err()吗?直接return nil是不是更好?
最后的案例中,g3里需要return ctx.Err()吗?直接return nil是不是更好?
这个没太大关系,因为 context
退出说明之前有 Goroutine 退出了,调用了 cancel 方法
“只要一个 goroutine 出错我们就不再等其他 goroutine 了,减少资源浪费”,这个我看 errgroup 源码理解应该是没有解决这个问题吧。
“只要一个 goroutine 出错我们就不再等其他 goroutine 了,减少资源浪费”,这个我看 errgroup 源码理解应该是没有解决这个问题吧。
我也发现了这个问题,是需要自己实现取消么?如果是的话怎么优雅的实现取消所有正在运行与尚未运行的协程。
“只要一个 goroutine 出错我们就不再等其他 goroutine 了,减少资源浪费”,这个我看 errgroup 源码理解应该是没有解决这个问题吧。
我也发现了这个问题,是需要自己实现取消么?如果是的话怎么优雅的实现取消所有正在运行与尚未运行的协程。
标准库/第三方库中涉及到io的库一般都会去主动判断ctx是否结束(case <-ctx.Done()
),一般不需要自己实现取消
Go并发编程(七) 深入理解 errgroup - Mohuishou
mohuishou 的 技术博客, 关注云原生, Go, K8s, Docker, 微服务等技术
https://lailin.xyz/post/go-training-week3-errgroup.html