更好用的Go同步并发任务实现

Golang的sync.WaitGroup在并发任务同步时处理错误较为复杂,而errgroup包提供了一种更优的解决方案。它允许在并发执行任务的同时检查每个任务的错误,并在有错误时立即停止执行。通过导入golang.org/x/sync/errgroup,创建Group对象,使用g.Go()执行带错误返回的函数。当所有任务完成或出现错误时,通过g.Wait()获取错误。示例代码展示了如何在main函数和单元测试中使用errgroup处理并发错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Golang提供了sync.WaitGroup 实现并发任务同步机制,但错误处理比较麻烦。因为协程任务可能会返回错误,要捕获错误通常需要适用channel同步消息。本文介绍来自Golang team的非官方包errgroup,相对sync.WaitGroup更好用,errgroup不仅可以并发地运行许多goroutine,还可以在管道中串行地运行它们,在进行下一个任务之前检查每个任务的错误。

要使用errgroup,需导入golang.org/x/sync/errgroup 包,然后定义:g := errgroup.Group{}。 对于每个并发任务调用方式为:g.Go(fun() error{}),我们看到g.Go()的参数是能返回错误的函数。如果没错返回nil,反之返回error,当有错时整个执行结束并给向同步函数抛出错误。模板代码为:

g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
    // do something
    return nil
})

err := g.Wait()

下面看一个示例:

package main

import (
	"fmt"
	"github.com/spf13/cast"
	"golang.org/x/sync/errgroup"
	"os"
	"time"
)

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

	g.Go(func() error {
		printLater("Hello\n", time.Millisecond*100)
		return nil
	})

	g.Go(func() error {
		//printLater("World\n", time.Millisecond*500)
		return fmt.Errorf("world failed")
	})

	g.Go(func() error {
		printLater(cast.ToString(os.Getpid())+"\n", time.Millisecond*100)
		return nil
	})

	err := g.Wait()
	if err != nil {
		fmt.Fprintf(os.Stderr, "error found: %s", err.Error())
		os.Exit(1)
	}
	fmt.Print("All work completed as expected")
}

func printLater(msg string, duration time.Duration) {
	time.Sleep(duration)
	fmt.Printf(msg)
}

输出结果:

16752
Hello
error found: world failed

下面再看一个errgroup包单元测试中包括的示例:

var (
	Web   = fakeSearch("web")
	Image = fakeSearch("image")
	Video = fakeSearch("video")
)

type Result string
type Search func(ctx context.Context, query string) (Result, error)

func fakeSearch(kind string) Search {
	return func(_ context.Context, query string) (Result, error) {
		return Result(fmt.Sprintf("%s result for %q", kind, query)), nil
	}
}

func ExampleGroup_parallel() {
	Google := func(ctx context.Context, query string) ([]Result, error) {
		g, ctx := errgroup.WithContext(ctx)

		searches := []Search{Web, Image, Video}
		results := make([]Result, len(searches))
		for i, search := range searches {
			i, search := i, search // 初始化闭包值
			g.Go(func() error {
				result, err := search(ctx, query)
				if err == nil {
					results[i] = result
				}
				return err
			})
		}
		if err := g.Wait(); err != nil {
			return nil, err
		}
		return results, nil
	}

	results, err := Google(context.Background(), "golang")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	for _, result := range results {
		fmt.Println(result)
	}


}

该实例模拟使用group同步单个并行任务,使用上下文和错误处理。输出结果为:

web result for "golang"
image result for "golang"
video result for "golang"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值