main.go
package main
import (
"context"
"errors"
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func task1(ctx context.Context) error {
fmt.Println("Task 1 started successfully")
select {
case <-time.After(1 * time.Second):
fmt.Println("Task 1 completed successfully")
return nil
case <-ctx.Done():
fmt.Println("Task 1 canceled")
return ctx.Err()
}
}
func task2(ctx context.Context) error {
fmt.Println("Task 2 started successfully")
select {
case <-time.After(2 * time.Second):
fmt.Println("Task 2 processed failed")
return errors.New("Task 2 processed failed due to error")
case <-ctx.Done():
fmt.Println("Task 2 canceled")
return ctx.Err()
}
}
func task3(ctx context.Context) error {
fmt.Println("Task 3 started successfully")
select {
case <-time.After(3 * time.Second):
fmt.Println("Task 3 completed successfully")
return nil
case <-ctx.Done():
fmt.Println("Task 3 canceled")
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return task1(ctx)
})
g.Go(func() error {
return task2(ctx)
})
g.Go(func() error {
return task3(ctx)
})
if err := g.Wait(); err != nil {
fmt.Println("Encountered error:", err)
cancel()
} else {
fmt.Println("All tasks completed successfully")
}
}
输出
Task 1 started successfully
Task 2 started successfully
Task 3 started successfully
Task 1 completed successfully
Task 2 processed failed
Encountered error: Task 2 processed failed due to error
ErrGroup vs WaitGroup
ErrGroup:
使用 ErrGroup 来管理并发任务中的错误。它聚合了所有协程中的错误,并返回遇到的第一个错误。
需要管理多个可能产生错误的并发任务。
想要利用上下文取消功能来优雅地关闭程序。
不想手动检查多个 WaitGroup 调用的错误
它与 Go 的上下文包无缝集成。任何来自协程的错误都会取消上下文,自动停止其他正在运行的任务。
WaitGroup
使用 WaitGroup 进行基本同步。它简单地等待指定数量的 goroutine 完成后再继续。
当你只关心任务完成而不预期错误时,它是理想的选择。
它不直接处理错误。你需要在每个 goroutine 中检查错误并单独处理它们。
基本用法 以下是一个简单的 errgroup 使用示例:
package main
import (
"fmt"
"net/http"
"golang.org/x/sync/errgroup"
)
func main() {
g := new(errgroup.Group)
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
for _, url := range urls {
url := url
g.Go(func() error {
resp, err := http.Get(url)
if err == nil {
resp.Body.Close()
}
return err
})
}
if err := g.Wait(); err == nil {
fmt.Println("Successfully fetched all URLs.")
}
}
带上下文的 errgroup
使用 WithContext 方法可以为 errgroup 提供一个上下文,当第一个任务返回错误时,会取消所有其他任务:
package main
import (
"context"
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
g, ctx := errgroup.WithContext(context.Background())
for i := 0; i < 4; i++ {
i := i
g.Go(func() error {
if i == 2 {
return fmt.Errorf("goroutine %d got an error", i)
}
select {
case <-time.After(500 * time.Millisecond):
fmt.Println("goroutine", i, "work done.")
case <-ctx.Done():
return ctx.Err()
}
return nil
})
}
if err := g.Wait(); err != nil {
fmt.Println(err.Error())
}
}
限制并发数量
errgroup 提供了 SetLimit 和 TryGo 方法来限制并发数量:
package main
import (
"fmt"
"errors"
"golang.org/x/sync/errgroup"
)
func main() {
eg := errgroup.Group{}
eg.SetLimit(2)
eg.TryGo(func() error {
fmt.Println("go1 run")
return nil
})
eg.TryGo(func() error {
return errors.New("go2 err")
})
eg.TryGo(func() error {
fmt.Println("go3 run")
return nil
})
if err := eg.Wait(); err != nil {
fmt.Println("err =", err)
}
}
使用场景
并发任务管理:当需要并发执行多个任务,并且需要处理任务中的错误时。
资源限制:通过 SetLimit 限制并发任务数量,避免资源耗尽。
优雅的错误传播:结合 context,当一个任务失败时,可以取消其他任务。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具