Go技巧:用 errgroup 优雅管理并发任务
在Go语言中处理并发任务时,我们经常面临一个痛点:如何同时管理多个goroutine的生命周期,并且统一处理它们返回的错误?
如果你还在用 sync.WaitGroup 加手动错误收集,那你就OUT了。官方扩展库 golang.org/x/sync/errgroup 是你必须掌握的利器。
痛点场景
假设我们需要并发请求三个API,只要有一个失败,就立即停止所有任务并返回错误。
传统的 WaitGroup 写法不仅需要手动处理channel传递错误,还要注意 close 的时机和协程泄漏问题,代码往往臃肿且容易出bug。
使用 errgroup 极简实现
package main
import (
"context"
"fmt"
"golang.org/x/sync/errgroup"
"net/http"
)
func main() {
g, ctx := errgroup.WithContext(context.Background())
urls := []string{
"https://baidu.com",
"https://bing.com",
"https://example.com",
}
for _, url := range urls {
url := url // 重要:捕获循环变量
g.Go(func() error {
// 监听 ctx,如果有任务失败,ctx会被取消
select {
case <-ctx.Done():
return ctx.Err()
default:
}
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
fmt.Printf("成功访问: %s, 状态码: %d\n", url, resp.StatusCode)
return nil
})
}
// 等待所有任务完成或第一个错误出现
if err := g.Wait(); err != nil {
fmt.Printf("任务执行出错: %v\n", err)
} else {
fmt.Println("所有任务完成")
}
}
核心技巧
- WithContext:创建一个带
context.Context的errgroup,当任意一个goroutine返回错误时,会自动取消这个上下文,从而通知其他任务优雅退出。 - g.Go:启动并发任务,无需手动管理
Add和Done。 - g.Wait:阻塞等待,并捕获第一个非nil的错误。
适用场景
- 批量RPC调用,快速失败。
- 并行处理文件,需要错误上报。
- 任何需要“全成功或全失败”的并发任务。
告别手写 WaitGroup,让 errgroup 帮你简化并发控制吧!