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

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"

你可能感兴趣的:(Golang,golang,errgroup,waitgroup)