Go Sync并发包之errgroup

你是否写过一个函数,它之所以很长,是因为它要完成很多任务,即使这些任务之间并不相互依赖? 你是否写过一个很长的函数,因为它要完成很多任务,即使这些任务并不相互依赖?我就遇到过这种情况。

想想看,你有一个函数可以做 3 件事:

  • 按用户 ID 从数据库中获取用户详细信息。
  • 调用外部服务,按用户 ID 获取用户最近的活动信息
  • 访问日志服务,按用户 ID 获取用户上次登录的详细信息
func FetchUserData(userID string) error {g := errgroup.Group{}// 获取用户详细信息userDetail, _ := fetchUserDetails(userID)// 获取用户活动userAct, _ := fetchUserActivity(userID)// 获取用户登录详细信息userLoginInfo, _ := fetchUserLoginDetails(userID)// ...
}

所有这些任务都只需要用户 ID,而不使用其他任务的数据。

您可以尝试使用 goroutines 来实现这一点,但您需要自己处理细节问题。让我们来回答这些问题:

  • 同步:如何确保一切完成?
  • 错误处理:如果一项任务出错,你该怎么办?或者三项任务中有两项不成功?
  • 取消:如果一个任务出现问题,如何停止所有其他正在运行的程序?
  • 限制:你是否考虑过限制同时运行多少个 goroutines?
  • 可重复使用:一旦找到解决方案,如何在类似情况下再次使用?

1.什么是 errgroup?

errgroup 软件包可让您同时处理多项任务。

通过它,可以轻松地以安全的方式一起运行,保持同步,处理错误,并控制何时停止 goroutines。下面是一个如何使用它的快速示例:

func FetchUserData() error {g := errgroup.Group{}// 获取用户详细信息g.Go(func() error {time.Sleep(1 * time.Second)fmt.Println("Fetched user details...")return nil})// 获取用户活动g.Go(func() error {time.Sleep(1 * time.Second)fmt.Println("Fetched user activity...")return nil})// 获取用户登录详细信息g.Go(func() error {time.Sleep(2 * time.Second)fmt.Println("Fetched user login details...")return nil})// 等待所有goroutines完成并返回第一个错误 (如果有)return g.Wait()
}

因此,errgroup 的工作就是运行这些任务,并通过 g.Wait() 等待任务结束,我们需要做的就是添加任务。
什么是 errgroup?

当你有很多任务时,比如 10 项、20 项甚至更多,这种方法就非常有用。

但有一点需要注意,如果不加以控制,同时运行过多的程序会占用资源。我们该如何处理呢?让我们在下一节中探讨。"

2.SetLimit:限制程序运行次数

这个软件包为我们提供了一种限制同时运行的 goroutines 数量的方法,让我们看看如何使用它:

func FetchUserData() error {g := errgroup.Group{}// 将限制设置为2g.SetLimit(2)// 获取用户详细信息g.Go(func() error {time.Sleep(1 * time.Second)fmt.Println("Fetched user details...")return nil})// 获取用户活动g.Go(func() error {time.Sleep(1 * time.Second)fmt.Println("Fetched user activity...")return nil})// 获取用户登录详细信息g.Go(func() error {time.Sleep(2 * time.Second)fmt.Println("Fetched user login details...")return nil})// 等待所有goroutines完成并返回第一个错误 (如果有)return g.Wait()
}

这样做可以确保只有 2 个程序同时运行。试试看,前两个任务会同时显示信息,而第三个任务会在启动 3 秒后显示信息。
SetLimit:限制程序数目

"设置限值后可以更改吗?

答案是肯定的,但要小心。

如果任何 goroutine 正在运行,试图更改限制将导致 errgroup.SetLimit() 异常。

现在,让我们再想一想,如果 errgroup 中已经有 50 个 goroutines,而你不想再添加更多的 goroutines,该怎么办?

3.TryGo:添加快速程序的受控方法

TryGo 与 Go 函数有相似之处,但它提供了一种细致入微的方法来处理 errgroup 中的 goroutine。具体来说,如果当前计数达到设定的限制,它就会阻止添加新的
goroutine

TryGo 的签名有点与众不同:

// TryGo: 检查并添加 goroutine
func (g *Group) TryGo(fn func () error) bool// Go: 只需添加一个 goroutine
func (g *Group) Go(fn func () error)

如果使用 TryGo,并成功将 goroutine 添加到 errgroup,它将通过返回 true 来传达成功。如果不成功,则返回 false。

但有趣的地方就在这里。

当一个 errgroup 已满时,errgroup.Go() 会阻塞直到一个 goroutine 结束,然后再添加一个新的,相反,errgroup.TryGo() 不会等待。如果 errgroup 已满,TryGo
会立即返回 false。

4.WithContext:处理取消

如果其中一个程序出错,如何停止所有正在运行的程序,从而避免浪费资源?

WithContext 函数用于创建新的 errgroup 和 context :

erg, ctx := errgroup.WithContext(context.Background())

该函数为您提供context,但不提供取消函数,因此您无法自行停止context。

"如果出现错误,errgroup 会为我停止所有程序吗?

不,errgroup 会取消上下文,但不会停止 goroutines。其余的 goroutines 会继续运行,直到完成为止,除非你这样做:

func main() {g, ctx := errgroup.WithContext(context.Background())g.Go(func() error { return fetchUserDetails(ctx) })g.Go(func() error { return fetchUserActivity(ctx) })g.Go(func() error { return fetchUserPaymentHistory(ctx) })// Wait for tasks to finish and print the errorif err := g.Wait(); err != nil {fmt.Printf("Encountered an error: %v\n", err)}
}

让这些任务依赖于上下文非常重要。因此,当上下文取消时,所有 goroutines 也将停止。

接下来,我将谈谈 Errgroup 的功能:Errgroup Explained:了解其内部运作。

这种方法不仅可以减少额外的代码,而且还提供了一种处理错误和控制 goroutines 生命周期的有效方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/644381.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Esp32-S3 进行JSON解析

之前介绍了esp32-s3的http通信,对于返回的结果进行解析也是必须的,通常我们可以使用json格式进行通信,这样即便于理解也便于取值。今天我们介绍下JSON解析。 在这里用到的库是ujson,代码如下,将如下代码保存到设备即可 import micropython import json from json import …

【错题集-编程题】数组中的最长连续子序列(排序 + 模拟)

牛客对应链接:数组中的最长连续子序列_牛客题霸_牛客网 (nowcoder.com) 一、分析题目 排序 模拟。 注意:值连续,位置可以不连续!小心处理数字相同的情况。 二、代码 //值得学习的代码 class Solution { public:int MLS(vecto…

python与上位机开发day02

1.常见运算符 1.1 赋值运算符 赋值运算符主要用来对变量进行赋值,包括如下这些: 运算符描述赋值加等于-减等于*乘等于/除等于//整除等于%模等于**幂等于 实例如下: a 10 a 5 # 等价于 a a5 a *2 # 等价于 a a*21.2 比较运算符 比较运算符主要用来比较两个数据的大小…

【Kafka】理论简介、消息队列(一)

简介 消息队列 为什么要有消息队列 图-1 消息队列的使用 消息队列 1)消息Message:网络中的两台计算机或者两个通讯设备之间传递的数据。例如说:文本、音乐、视频等内容。 2)队列Queue:一种特殊的线性表(数据元素首尾相接),特殊…

「React Native」为什么要选择 React Native 作为的跨端方案

文章目录 前言一、常见因素二、举个栗子2.1 项目背景2.2 为什么选择 React Native2.3 项目实施2.4 成果总结 前言 没有完美的跨端技术,只有适合的场景。脱离适用场景去谈跨端技术没有什么意义。 一、常见因素 共享代码库: React Native 允许开发者编写…

新手必看十大地推网推拉新平台,副业从此不求人

现如今,各种各样的APP平台到处充斥在我们的日常生活之中,出门有打车软件,吃饭有点外卖软件,看剧有影视软件,打发时间有短视频软件等等。而很多软件基本都需要通过地推网推拉新来营销推广,因此诞生了一个庞大…

OpenWrt One/AP-24.XY 开源路由器发布,OpenWRT与Banana Pi社区合作

OpenWrt One/AP-24.XY 开源路由器 2024 年,OpenWrt 项目将迎来20 周年!OpenWrt 开源社区官方通过推出社区自己的第一个完全上游支持的硬件设计来庆祝这一周年纪念日。并与联发科,Banana Pi开源社区紧密合作,共同完成硬件的设计与…

人工智能基础-Python之Pandas库教程

文章目录 前言一、Pandas是什么?二、使用步骤1.引入库2.数据读取2.1 数据类型2.2 数据读取1.常见操作2.txt读取 3.pandas的数据结构3.1 Series1.属性2.创建Series3.查询 3.2 DataFrame1.创建DataFrame 4.查询数据4.1 data.loc 根据行列标签值进行查询1.使用单个labe…

Hive数据类型

1.基本数据类型 示例: -- 创建表并定义列的数据类型 CREATE TABLE data_types_example (tinyint_column TINYINT,smallint_column SMALLINT,int_column INT,bigint_column BIGINT,boolean_column BOOLEAN,float_column FLOAT,double_column DOUBLE,string_column S…

【Java框架】SpringBoot(二)核心配置

目录 yml文件什么是yml文件yml文件的优点使用场景脚本语言序列化配置文件 yml的基本语法yml支持的数据类型字面量对象(属性和值)、Map(键值对)数组复合结构 Spring Boot配置文件的值注入第一种读取方式Value第二种读取方式ConfigurationProperties第三种读取方式自动装配Enviro…

hyperf 三十一 极简DB组件

一 安装及配置 composer require hyperf/db php bin/hyperf.php vendor:publish hyperf/db 默认配置 config/autoload/db.php 如下,数据库支持多库配置,默认为 default。 配置项类型默认值备注driverstring无数据库引擎 支持 pdo 和 mysqlhoststringl…

【软件安装】(十六)双系统Ubuntu22.04引导启动菜单的默认项

一个愿意伫立在巨人肩膀上的农民...... 好学的人总是喜欢在电脑上安装双系统,可是安装好系统之后,就会出现默认启动优先级的苦恼,如果在Bios中设置Windows引导启动为优先启动,那么每次想要进如Ubuntu系统就都需要重新设置Bios。如…