日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

go中errgroup源碼解讀

 Coder編程 2022-11-11 發(fā)布于北京

errgroup

前言

來看下errgroup的實現(xiàn)

如何使用

func main() {
	var eg errgroup.Group

	eg.Go(func() error {
		return errors.New("test1")
	})

	eg.Go(func() error {
		return errors.New("test2")
	})

	if err := eg.Wait(); err != nil {
		fmt.Println(err)
	}
}

類比于waitgroup,errgroup增加了一個對goroutine錯誤收集的作用。

不過需要注意的是:

errgroup返回的第一個出錯的goroutine拋出的err。

errgroup中還可以加入context

func main() {
	eg, ctx := errgroup.WithContext(context.Background())

	eg.Go(func() error {
		// test1函數(shù)還可以在啟動很多goroutine
		// 子節(jié)點都傳入ctx,當test1報錯,會把test1的子節(jié)點一一cancel
		return test1(ctx)
	})

	eg.Go(func() error {
		return test1(ctx)
	})

	if err := eg.Wait(); err != nil {
		fmt.Println(err)
	}
}

func test1(ctx context.Context) error {
	return errors.New("test2")
}

實現(xiàn)原理

代碼很簡單

type Group struct {
	// 一個取消的函數(shù),主要來包裝context.WithCancel的CancelFunc
	cancel func()

	// 還是借助于WaitGroup實現(xiàn)的
	wg sync.WaitGroup

	// 使用sync.Once實現(xiàn)只輸出第一個err
	errOnce sync.Once

	// 記錄下錯誤的信息
	err     error
}

還是在WaitGroup的基礎上實現(xiàn)的

WithContext

// 返回一個被context.WithCancel重新包裝的ctx

func WithContext(ctx context.Context) (*Group, context.Context) {
	ctx, cancel := context.WithCancel(ctx)
	return &Group{cancel: cancel}, ctx
}

里面使用了context,通過context.WithCancel對傳入的context進行了包裝

WithCancel函數(shù)返回的CancelFunc被調(diào)用或者是父節(jié)點的done channel被關閉(父節(jié)點的 CancelFunc 被調(diào)用),此 context(子節(jié)點)的 done channel 也會被關閉。

errgroup把返回的CancelFunc包進了自己的cancel中,來實現(xiàn)對使用errgroupctx啟動的goroutine的取消操作。

Go

// 啟動取消阻塞的goroutine
// 記錄第一個出錯的goroutine的err信息
func (g *Group) Go(f func() error) {
	// 借助于waitgroup實現(xiàn)
	g.wg.Add(1)

	go func() {
		defer g.wg.Done()

		// 執(zhí)行出錯
		if err := f(); err != nil {
			// 通過sync.Once記錄下第一個出錯的err信息
			g.errOnce.Do(func() {
				g.err = err
				// 如果包裝了cancel,也就是context的CancelFunc,執(zhí)行退出操作
				if g.cancel != nil {
					g.cancel()
				}
			})
		}
	}()
}

1、借助于waitgroup實現(xiàn)對goroutine阻塞;

2、通過sync.Once記錄下,第一個出錯的goroutine的錯誤信息;

3、如果包裝了contextCancelFunc,在出錯的時候進行退出操作。

Wait

// 阻塞所有的通過Go加入的goroutine,然后等待他們一個個執(zhí)行完成
// 然后返回第一個出錯的goroutine的錯誤信息
func (g *Group) Wait() error {
	// 借助于waitgroup實現(xiàn)
	g.wg.Wait()
	// 如果包裝了cancel,也就是context的CancelFunc,執(zhí)行退出操作
	if g.cancel != nil {
		g.cancel()
	}
	return g.err
}

1、借助于waitgroup實現(xiàn)對goroutine阻塞;

2、如果包裝了contextCancelFunc,在出錯的時候進行退出操作;

3、拋出第一個出錯的goroutine的錯誤信息。

錯誤的使用

不過工作中發(fā)現(xiàn)一個errgroup錯誤使用的例子

func main() {
	eg := errgroup.Group{}
	var err error
	eg.Go(func() error {
		// 處理業(yè)務
		err = test1()
		return err
	})

	eg.Go(func() error {
		// 處理業(yè)務
		err = test1()
		return err
	})

	if err = eg.Wait(); err != nil {
		fmt.Println(err)
	}
}

func test1() error {
	return errors.New("test2")
}

很明顯err被資源競爭了

$ go run -race main.go 
==================
WARNING: DATA RACE
Write at 0x00c0000801f0 by goroutine 8:
  main.main.func2()
      /Users/yj/Go/src/Go-POINT/sync/errgroup/main.go:23 +0x97
...

總結

errgroup相比比較簡單,不過需要先弄明白waitgroup,context以及sync.Once,主要是借助這幾個組件來實現(xiàn)的。

errgroup可以帶攜帶context,如果包裝了context,會使用context.WithCancel進行超時,取消或者一些異常的情況

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多