Go 并发控制 errgroup.Group

errgroup (golang.org/x/sync/errgroup) 为一组协程 goroutines 在执行共同任务的子任务时,提供同步、错误传播和上下文取消的功能。

对于要等待 n 个线程完成后再进行下一步的同步操作的做法,常见使用 sync.WaitGroup 来等待一组事件。

errgroup.Group 与 sync.WaitGroup 类似,但它增加了对返回错误任务的处理能力,以及限制协程并发数的能力

使用方法与 WaitGroup 类似,只是封装了 WaitGroup 的 Add() 和 Wait() 方法,解决 WaitGroup 无法返回错误的问题。

go 复制代码
package main

import (
 "fmt"
 "time"

 "golang.org/x/sync/errgroup"
)

func main() {

 g := &errgroup.Group{}
 for i := 0; i < 5; i++ {
  index := i
  g.Go(func() error {
   fmt.Printf("start to execute the %d gorouting\n", index)
   time.Sleep(time.Duration(index) * time.Second)
   if index%2 == 0 {
    return fmt.Errorf("something has failed on grouting:%d", index)
   }
   fmt.Printf("gorouting:%d end\n", index)
   return nil
  })
 }
 if err := g.Wait(); err != nil {
  fmt.Println(err)
 }
}

// Output:
// start to execute the 4 gorouting
// start to execute the 1 gorouting
// start to execute the 0 gorouting
// start to execute the 2 gorouting
// start to execute the 3 gorouting
// gorouting:1 end
// gorouting:3 end
// something has failed on grouting:0

如果多个 goroutine 出现错误,errgroup 只会获取到第一个出错的 goroutine 的错误信息。不管是否有协程执行失败,wait() 都要等待所有协程执行完成。

支持 context :

css 复制代码
g, _ := errgroup.WithContext(context.Background()) // 支持 context

Wait() 方法可多次调用,依然可以得到 group 的 error 信息:

go 复制代码
 ...
 if err := g.Wait(); err != nil {
  fmt.Println(err)
 }

 if err := g.Wait(); err != nil { // 可再次调用 Wait,依然可以得到 group 的 error 信息
  fmt.Println(err)
 }

限制最大并发数:

SetLimit() 方法用于限制该组中最多同时运行的 goroutine 数量,参数代表的是当前同时处于活动状态(处理业务)的 goroutine 的最多数量。

go 复制代码
package main

import (
 "log"
 "time"

 "golang.org/x/sync/errgroup"
)

func main() {
 jobs := make(chan int, 10)
 go func() {
  for i := 0; i < 8; i++ {
   jobs <- i + 1
  }
  close(jobs)
 }()

 eg:= &errgroup.Group{}
 eg.SetLimit(3)

 for j := range jobs {
  j := j
  eg.Go(func() error {
   log.Printf("handle job: %d\n", j)
   time.Sleep(2 * time.Second)
   return nil
  })
 }
 eg.Wait()
}

上面的示例创建了一组 goroutines 来处理 job,同一时间允许最多 3 个 goroutine 处于活动状态。

Output:

yaml 复制代码
2024/12/17 18:28:19 handle job: 3
2024/12/17 18:28:19 handle job: 1
2024/12/17 18:28:19 handle job: 2
2024/12/17 18:28:21 handle job: 4
2024/12/17 18:28:21 handle job: 6
2024/12/17 18:28:21 handle job: 5
2024/12/17 18:28:23 handle job: 7
2024/12/17 18:28:23 handle job: 8

从示例运行结果中的时间戳我们可以看到:虽然我们创建了很多 goroutine,但同一时间内处理活动状态(正在处理 job)的 goroutine 的数量最多为 3 个。

其内部实现就是将带缓冲 channel 用作计数信号量 (counting semaphore) 来限制最大并发数。

相关推荐
希望永不加班4 小时前
SpringBoot 事件机制:ApplicationEvent 与监听器
java·开发语言·spring boot·后端·spring
IVAN不想说话4 小时前
为什么 Karpathy 的「LLM Wiki」突然火了?
后端
Nyarlathotep01134 小时前
自动内存管理(2):垃圾收集器与内存分配策略
java·jvm·后端
人活一口气5 小时前
Spring Boot 3.2 + GraalVM原生镜像:启动速度从秒到毫秒的极致优化
后端
小江的记录本5 小时前
【Transformer架构】Transformer架构核心知识体系(包括自注意力机制、多头注意力、Encoder-Decoder结构)
java·人工智能·后端·python·深度学习·架构·transformer
zs宝来了5 小时前
etcd Raft 实现:分布式一致性核心原理
golang·go·后端技术
LucianaiB5 小时前
【邪修 QClaw】女朋友说我说话太直,我直接用QClaw来解决问题!
后端
疯狂的程序猴5 小时前
Flutter应用代码混淆完整指南:Android与iOS平台配置详解
后端·ios
gelald5 小时前
SpringBoot - 配置加载
spring boot·后端·spring