golang errgroup

errgroup是Go语言中用于管理一组goroutine并处理其错误的工具,它提供了WithContext、Go、TryGo和Wait等方法,支持上下文取消和错误传播。在多文件MD5计算的示例中,errgroup确保在任何goroutine出错时能立即停止其他goroutine的执行并返回错误。

errgroup为goroutine组提供了通知、错误传递、上下文取消功能。

一般在一组goroutine出现错误需要取消全部goroutine的情况使用到

成员与方法

errGroup主要有以下结构

  • cancel func() : context的cancel方法
  • wg sync.WiatGroup: 封装sync.WaitGroup
  • errOnce: 保证只接受一次错误
  • err error: 保存第一个返回的错误
  • sem chan token: 控制goroutine活动数量(token实际上是struct{})

有以下方法

  • WithContext(ctx context.Context) (*Group, context.Context): 使传入context转换为一个可取消的context,并将cancel方法传入errGroup

    func WithContext(ctx context.Context) (*Group, context.Context) {
        ctx, cancel := context.WithCancel(ctx)
        return &Group{cancel: cancel}, ctx
    }
    
  • Go(f func() error): 保存errGroup传入方法f的第一次错误,并取消errGroup中的context

    func (g *Group) Go(f func() error) {
    	if g.sem != nil {
    		g.sem <- token{}
    	}
    
    	g.wg.Add(1)
    	go func() {
    		defer g.done()
    
    		if err := f(); err != nil {
    			g.errOnce.Do(func() {
    				g.err = err
    				if g.cancel != nil {
    					g.cancel()
    				}
    			})
    		}
    	}()
    }
    
    func (g *Group) done() {
    	if g.sem != nil {
    		<-g.sem
    	}
    	g.wg.Done()
    }
    
  • TryGo(f func() error) bool: 能返回开启协程额度是否足够的Go(f func() error)方法

  • Wait() error: 等待errGroup Go中方法运行结束

    func (g *Group) Wait() error {
        g.wg.Wait()
        if g.cancel != nil {
            g.cancel()
        }
        return g.err
    }
    
  • SetLimit(n int): 设置限制活动goroutine的最大数量(负数代表没有限制)。通过固定长度n的channel是实现的,setLimit设置固定额度,每个活动的goroutine消费该额度,然后处理完之后释放额度

    func (g *Group) SetLimit(n int) {
    	if n < 0 {
    		g.sem = nil
    		return
    	}
    	if len(g.sem) != 0 {
    		panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
    	}
    	g.sem = make(chan token, n)
    }
    

示例

多协程计算多文件md5 sum

以下是官方利用errGroup开启1个goroutine检查文件和20个goroutine遍历目标路径文件计算md5 sum。若只要有一个文件检查出现就直接返回错误,终止后面20个goroutine的sum计算

func ExampleGroup_pipeline() {
	m, err := MD5All(context.Background(), ".")
	if err != nil {
		log.Fatal(err)
	}

	for k, sum := range m {
		fmt.Printf("%s:\t%x\n", k, sum)
	}
}

type result struct {
	path string
	sum  [md5.Size]byte
}

// MD5All reads all the files in the file tree rooted at root and returns a map
// from file path to the MD5 sum of the file's contents. If the directory walk
// fails or any read operation fails, MD5All returns an error.
func MD5All(ctx context.Context, root string) (map[string][md5.Size]byte, error) {
	// ctx is canceled when g.Wait() returns. When this version of MD5All returns
	// - even in case of error! - we know that all of the goroutines have finished
	// and the memory they were using can be garbage-collected.
	g, ctx := errgroup.WithContext(ctx)
	paths := make(chan string)

	g.Go(func() error {
		defer close(paths)
		return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.Mode().IsRegular() {
				return nil
			}
			select {
			case paths <- path:
			case <-ctx.Done():
				return ctx.Err()
			}
			return nil
		})
	})

	// Start a fixed number of goroutines to read and digest files.
	c := make(chan result)
	const numDigesters = 20
	for i := 0; i < numDigesters; i++ {
		g.Go(func() error {
			for path := range paths {
				data, err := ioutil.ReadFile(path)
				if err != nil {
					return err
				}
				select {
				case c <- result{path, md5.Sum(data)}:
				case <-ctx.Done():
					return ctx.Err()
				}
			}
			return nil
		})
	}
	go func() {
		g.Wait()
		close(c)
	}()

	m := make(map[string][md5.Size]byte)
	for r := range c {
		m[r.path] = r.sum
	}
	// Check whether any of the goroutines failed. Since g is accumulating the
	// errors, we don't need to send them (or check for them) in the individual
	// results sent on the channel.
	if err := g.Wait(); err != nil {
		return nil, err
	}
	return m, nil
}

### Golang 第三方库推荐 以下是几个常用的 Golang 第三方库及其功能介绍: #### 数据库操作 - **gorm**: 这是一个流行的 ORM 库,用于简化数据库交互。它支持多种数据库,并提供了丰富的特性来处理复杂的查询需求[^4]。 ```go import "gorm.io/gorm" type User struct { ID uint `gorm:"primarykey"` Name string Age uint } ``` #### 文件操作 - **Excelize**: 如果需要处理 Excel 文档,`Excelize` 是一个非常强大的工具。它可以轻松读取、修改和创建 Excel 文件[^5]。 ```go package main import ( "fmt" "github.com/xuri/excelize/v2" ) func main() { f := excelize.NewFile() defer func() { _ = f.Close() }() err := f.SetCellValue("Sheet1", "A1", "Hello, World!") if err != nil { fmt.Println(err) } } ``` #### HTTP 请求 - **fasthttp**: 它是一种高性能的 HTTP 实现方式,在高并发场景下表现尤为突出。相比标准库中的 net/http,它的性能更高[^1]。 ```go import "github.com/valyala/fasthttp" func handler(ctx *fasthttp.RequestCtx) { ctx.WriteString("Hello, world!\n") } func main() { fasthttp.ListenAndServe(":8080", handler) } ``` #### 日志记录 - **zap**: 提供了一种快速且结构化的日志记录方法,适合生产环境下的应用监控和调试。 ```go import ( "go.uber.org/zap" ) func initLogger() (*zap.Logger, error) { config := zap.NewProductionConfig() logger, err := config.Build() return logger, err } ``` #### 并发控制 - **errgroup**: 可以帮助管理多个 Goroutine 的执行状态以及错误传播问题,从而实现优雅退出机制。 ```go import ( "context" "google.golang.org/sync/errgroup" ) func doWork(group *errgroup.Group, id int) { group.Go(func() error { // Simulate work. return nil }) } func main() { var g errgroup.Group for i := 0; i < 5; i++ { doWork(&g, i) } if err := g.Wait(); err != nil { log.Fatal(err) } } ``` 这些库不仅增强了 Go 语言的功能范围,还促进了其生态系统的繁荣发展[^2]。当开发者想要引入某个特定库到自己的项目时,可以通过命令行工具完成依赖项安装过程[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值