用同步errgroup取消所有正在运行的goroutines(附实例)

542 阅读1分钟

在这个例子中,如果至少有一个程序发生错误,我们将取消所有正在运行的goroutine。为此,我们将使用sync 包中的errgrouperrgroup 是为作为子任务工作的一组goroutines的同步、错误传播和Context取消而创建的。

例子

我们将运行3个goroutine。如果其中任何一个延迟超过80毫秒才完成自己的工作,我们将强制它返回一个错误,因为我们很着急。如果发生这种情况,所有正在运行的goroutines也将被终止。否则,显然所有的工作都会顺利进行。

package main

import (
	"context"
	"fmt"
	"math/rand"
	"time"

	"golang.org/x/sync/errgroup"
)

func main() {
	fmt.Println(">> START")

	// Prevent picking up the same random number all the time for sleeping.
	rand.Seed(time.Now().UnixNano())

	goErrGroup, ctx := errgroup.WithContext(context.Background())

	goErrGroup.Go(func() error {
		return doSomething(ctx, 1)
	})
	goErrGroup.Go(func() error {
		return doSomething(ctx, 2)
	})
	goErrGroup.Go(func() error {
		return doSomething(ctx, 3)
	})
	goErrGroup.Go(func() error {
		return doSomething(ctx, 4)
	})

	// Wait for the first error from any goroutine.
	if err:= goErrGroup.Wait(); err != nil {
		fmt.Println(err)
	}

	fmt.Println(">> FINISH")
}

func doSomething(ctx context.Context, id int) error  {
	fmt.Printf("STR: gorotinne %d\n", id)

	// Pick a random number to simulate time it takes to finish the job.
	delay := rand.Intn(100)
	if delay > 80 {
		return fmt.Errorf("FAIL: gorotinne %d: %dms", id, delay)
	}
	time.Sleep(time.Duration(delay) * time.Millisecond)

	fmt.Printf("END: gorotinne %d\n", id)

	return nil
}

测试

在这个例子中,所有的goroutines都设法完成了它们正在做的事情:

>> START
STR: gorotinne 1
STR: gorotinne 2
STR: gorotinne 4
STR: gorotinne 3
END: gorotinne 3
END: gorotinne 1
END: gorotinne 4
END: gorotinne 2
>> FINISH

在这个例子中,只有goroutine 1和4成功地完成了他们正在做的事情:

>> START
STR: gorotinne 1
STR: gorotinne 2
STR: gorotinne 3
STR: gorotinne 4
END: gorotinne 1
END: gorotinne 4
FAIL: gorotinne 2: 99ms
>> FINISH

这个例子在一开始就失败了,所以没有一个goroutine有机会完成他们正在做的事情:

>> START
STR: gorotinne 1
STR: gorotinne 2
STR: gorotinne 3
STR: gorotinne 4
FAIL: gorotinne 1: 81ms
>> FINISH