首页 > 其他分享 >golang常用包详解之: errgroup

golang常用包详解之: errgroup

时间:2023-11-30 13:06:36浏览次数:36  
标签:err errgroup goroutine sync golang 详解 context Go

前言:

并发编程在现代软件开发中变得越来越重要。Go语言通过goroutine和channel等语言特性为并发编程提供了非常强大的支持,但是在实际开发中,如何有效管理多个goroutine并处理它们可能产生的错误是一个挑战。这时,Go语言的官方库中的errgroup包就能发挥作用。

正文:

1. errgroup包概述

errgroup包提供了一种方便的方式来跟踪和处理多个goroutine中的错误。它可以让你启动多个goroutine,并等待它们全部完成,或者在任何一个goroutine返回错误时立即取消所有其他goroutine。

2. errgroup包源码分析

errgroup包的源码中,它主要使用了sync.WaitGroupcontext.Context来实现多个goroutine的管理和错误处理。

  • errgroup.Group结构体定义了一个用于管理goroutine的组,并包含一个sync.WaitGroup类型的成员变量wg用于等待所有goroutine完成。
  • Group.Go()方法会创建一个新的goroutine,并在其中执行传入的函数。同时,它会使用sync.WaitGroup.Add(1)增加计数器,表示有一个goroutine正在执行。
  • Go()方法中,通过recover()函数捕获可能发生的panic,并将其转换为错误类型返回。
  • Wait()方法会等待所有goroutine执行完毕,并通过sync.WaitGroup.Wait()来阻塞主线程,直到所有goroutine都完成。如果其中一个goroutine返回了错误,它会通过context对象取消其他正在执行的goroutine,并返回错误。

3. errgroup包使用方法

下面列举了一些常见的使用场景和例子,展示了如何在不同情况下使用errgroup包:

  • 示例0:超时返回
package main

  import (
  	"context"
  	"fmt"
  	"golang.org/x/sync/errgroup"
  	"time"
  )

  func main() {
  	// 创建一个带有取消信号的context
  	ctx, cancel := context.WithCancel(context.Background())
  	defer cancel()

  	// 通过errgroup.WithContext创建带有context的errgroup.Group
  	g, ctx := errgroup.WithContext(ctx)

  	// 添加并发任务
  	g.Go(func() error {
  		select {
  		case <-ctx.Done():
  			fmt.Println("任务1被取消")
  			return ctx.Err()
  		case <-time.After(2 * time.Second):
  			fmt.Println("任务1完成")
  			return nil
  		}
  	})

  	g.Go(func() error {
  		select {
  		case <-ctx.Done():
  			fmt.Println("任务2被取消")
  			return ctx.Err()
  		case <-time.After(3 * time.Second):
  			fmt.Println("任务2完成")
  			return nil
  		}
  	})

  	// 等待所有并发任务完成
  	if err := g.Wait(); err != nil {
  		fmt.Println("任务执行出错:", err)
  	} else {
  		fmt.Println("所有任务执行完成")
  	}
  }
  ```

- 示例1:并发执行多个HTTP请求,并等待它们全部完成或任何一个请求返回错误。

```go
package main

import (
	"fmt"
	"net/http"

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

func main() {
	g := new(errgroup.Group)

	urls := []string{
		"http://example.com",
		"http://example.net",
		"http://example.org",
	}

	for _, url := range urls {
		url := url // create a new variable to avoid the closure problem
		g.Go(func() error {
			resp, err := http.Get(url)
			if err != nil {
				return err
			}
			defer resp.Body.Close()
			// process the response
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		fmt.Println("one of the requests returned an error:", err)
	}
}
  • 示例2:并发执行一组文件读取操作,并等待它们全部完成或任何一个读取操作返回错误。
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

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

func main() {
	g := new(errgroup.Group)

	files := []string{
		"file1.txt",
		"file2.txt",
		"file3.txt",
	}

	for _, filename := range files {
		filename := filename // create a new variable to avoid the closure problem
		g.Go(func() error {
			file, err := os.Open(filepath.Join("path/to/files", filename))
			if err != nil {
				return err
			}
			defer file.Close()

			data, err := ioutil.ReadAll(file)
			if err != nil {
				return err
			}

			// process the file data
			fmt.Println(string(data))
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		fmt.Println("one of the file reads returned an error:", err)
	}
}
  • 示例3:并发执行一组耗时的计算任务,并限制最大同时执行的goroutine数。
package main

import (
	"fmt"
	"math/rand"
	"time"

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

func main() {
	g, ctx := errgroup.WithContext(context.Background())

	maxWorkers := 5 // maximum number of goroutines to run concurrently

	for i := 0; i < maxWorkers; i++ {
		g.Go(func() error {
			// Perform some time-consuming computation
			result := compute()
			fmt.Println(result)
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		fmt.Println("one of the computations returned an error:", err)
	}
}

func compute() int {
	// Simulate some time-consuming computation
	time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
	return rand.Intn(100)
}

这些示例展示了errgroup包在不同场景下的灵活应用,你可以根据实际需求使用errgroup包来管理和处理多个goroutine的错误。

后记:

errgroup包是Go语言标准库中非常实用的一个包,它可以帮助我们更好地管理多个并发程序。本文介绍了errgroup包的基本概念、使用方法和源码分析,希望能够帮助你更好地理解并发编程中的错误处理。

博客中所涉及到的图片都有版权,请谨慎使用



标签:err,errgroup,goroutine,sync,golang,详解,context,Go
From: https://blog.51cto.com/u_10624715/8627896

相关文章

  • 神经网络入门篇:详解深层网络中的前向传播(Forward propagation in a Deep Network)
    深层网络中的前向传播先说对其中一个训练样本\(x\)如何应用前向传播,之后讨论向量化的版本。第一层需要计算\({{z}^{[1]}}={{w}^{[1]}}x+{{b}^{[1]}}\),\({{a}^{[1]}}={{g}^{[1]}}{({z}^{[1]})}\)(\(x\)可以看做\({{a}^{[0]}}\))第二层需要计算\({{z}^{[2]}}={{w}^{[2]}}{{a}^{[......
  • SQL HAVING 子句详解:在 GROUP BY 中更灵活的条件筛选
    SQLHAVING子句HAVING子句被添加到SQL中,因为WHERE关键字不能与聚合函数一起使用。HAVING语法SELECTcolumn_name(s)FROMtable_nameWHEREconditionGROUPBYcolumn_name(s)HAVINGconditionORDERBYcolumn_name(s);演示数据库以下是Northwind示例数据库中“Customers......
  • Java集合框架主要接口及实现类详解
    Java集合框架是Java编程语言提供的一组接口,用于处理对象集合。Java集合框架中包括了一系列的接口、实现类和算法,可以方便地操作和管理各种类型的集合数据。Java集合框架主要包括以下接口:Collection接口:是所有集合接口的根接口,提供了基本的集合操作,如添加、删除、遍历等操作。L......
  • SQL HAVING 子句详解:在 GROUP BY 中更灵活的条件筛选
    SQLHAVING子句HAVING子句被添加到SQL中,因为WHERE关键字不能与聚合函数一起使用。HAVING语法SELECTcolumn_name(s)FROMtable_nameWHEREconditionGROUPBYcolumn_name(s)HAVINGconditionORDERBYcolumn_name(s);演示数据库以下是Northwind示例数据库中“Customers......
  • Golang-常见数据结构实现原理
    chan 1.chan数据结构 src/runtime/chan.go:hchan定义了channel的数据结构:typehchanstruct{qcountuint//当前队列中剩余元素个数dataqsizuint//环形队列长度,即可以存放的元素个数bufunsafe.Pointer//环形队列指针......
  • C#中TimeSpan和DateTime的用法详解
    在C#编程中,TimeSpan和DateTime是常用的日期和时间处理类。它们提供了丰富的方法和属性,方便我们对日期和时间进行操作和格式化。本篇博客将详细介绍TimeSpan和DateTime的用法。TimeSpanTimeSpan类用于表示一段时间间隔,可以表示从几天到几个纳秒的时间。下面是TimeSpan类的常用属......
  • golang-切片
    引子因为数组的长度是固定的并且数组的长度属于类型的的一部分,所以数组有很多的局限性,例如:funcarraySum(x[3]int)int{sum:=0for_,v:=rangex{sum=sum+v}returnsum}这个求和函数稚嫩接收长度为[3]int的数组元素,其他的都不支持......
  • 详解Redis三大集群模式,轻松实现高可用!
    原文:https://zhuanlan.zhihu.com/p/624144774?utm_id=01.Redis集群简介1.1什么是Redis集群Redis集群是一种通过将多个Redis节点连接在一起以实现高可用性、数据分片和负载均衡的技术。它允许Redis在不同节点上同时提供服务,提高整体性能和可靠性。根据搭建的方式和集群的特性,R......
  • 使用Golang构建高性能网络爬虫
    前段时间和以前公司的老同事聚会,喝酒中无意聊到目前他们公司在做的一个爬虫项目,因为效率低下,整个人每天忙的不可开交。借着这次聚会,正好询问我一些解决方案。于是,我给了他们我的一些思路。所谓的高性能网络爬虫就是一种能够快速、高效地从互联网上抓取大量网页数据的程序。网络爬虫......
  • Golang Gin 获取Restful参数、URL查询参数,Form 表单参数,JSON格式参数
    前言http请求中,可以通过URL查询参数提交数据到服务器,可以通过post的json方式,还有一直方式就是Form表单。Form表单相比URL查询参数,用户体验好,可以承载更多的数据,尤其是文件上传时,特别方便。这里推荐飞雪无情的博客;写了一些列的gin的使用教程,很时候新手学习如果想对gin有一个完整......