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"