返回

Go扩展并发原语之errgroup.Group的实战与源码解读

后端

引言

在Go中,我们经常会遇到多线程并发处理任务的情况。为了更好地管理和处理这些并发任务,Go语言提供了许多内置的并发原语,其中errgroup.Group是一个非常有用的工具。它可以让我们将多个并发任务分组,并对每个组的结果进行汇总,从而简化了并发任务的管理和错误处理。

errgroup.Group原语的使用

要使用errgroup.Group,首先需要创建一个Group对象。然后,我们可以使用Group.Add()方法将并发任务添加到组中。当所有任务都完成后,我们可以使用Group.Wait()方法等待所有任务完成。如果任何一个任务发生错误,Group.Wait()方法会返回一个错误,我们可以使用Group.Error()方法获取错误信息。

下面是一个简单的示例,展示了如何使用errgroup.Group原语:

package main

import (
	"context"
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	// 创建一个errgroup.Group对象
	var group errgroup.Group

	// 向group中添加三个并发任务
	for i := 0; i < 3; i++ {
		group.Add(1)
		go func(i int) {
			defer group.Done()
			// 模拟任务执行
			time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
			if i == 2 {
				// 模拟错误发生
				err := errors.New("task 2 failed")
				group.Error(err)
			}
			fmt.Printf("task %d finished\n", i)
		}(i)
	}

	// 等待所有任务完成
	if err := group.Wait(); err != nil {
		fmt.Println("An error occurred:", err)
	} else {
		fmt.Println("All tasks completed successfully")
	}
}

在这个示例中,我们创建了一个errgroup.Group对象,并将三个并发任务添加到组中。每个任务模拟一个耗时的操作,并随机生成一个错误。最后,我们使用Group.Wait()方法等待所有任务完成,并根据是否有错误发生来输出结果。

errgroup.Group源码解读

为了更好地理解errgroup.Group原语的实现原理,我们接下来对它的源码进行解读。

errgroup.Group的源码位于Go语言标准库的sync包中,文件路径为:

$GOROOT/src/sync/errgroup/errgroup.go

在errgroup.Group的源码中,我们首先看到一个类型定义:

type Group struct {
	wg sync.WaitGroup
	errOnce sync.Once
	err error
}

这个结构体包含了一个sync.WaitGroup对象、一个sync.Once对象和一个error对象。sync.WaitGroup对象用于计数并发任务的数量,sync.Once对象用于保证errgroup.Group对象只记录第一个错误,error对象用于存储errgroup.Group对象中发生的第一个错误。

接下来,我们看到几个重要的函数实现:

func (g *Group) Add(delta int) {
	g.wg.Add(delta)
}

func (g *Group) Done() {
	g.wg.Done()
}

func (g *Group) Wait() error {
	g.wg.Wait()
	return g.err
}

func (g *Group) Error(err error) {
	g.errOnce.Do(func() { g.err = err })
}
  • Add()方法用于增加WaitGroup计数。
  • Done()方法用于减少WaitGroup计数。
  • Wait()方法用于等待WaitGroup计数变为0,并返回errgroup.Group对象中发生的第一个错误。
  • Error()方法用于将错误记录到errgroup.Group对象中。

结语

通过对errgroup.Group原语的实操应用和源码解读,我们对errgroup.Group有了更加深入的了解。我们学习了如何使用errgroup.Group来有效地管理和处理多线程并发任务,以及如何通过errgroup.Group的源码来理解它的实现原理。希望这篇博文能对您有所帮助,欢迎在评论区留言讨论。

拓展阅读