ErrGroup

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() // Ensure cancellation happens when main() exits

 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) // 设置并发数量为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,当一个任务失败时,可以取消其他任务。
posted @   朝阳1  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示