成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go 語言 Errgroup 庫的使用方式和實現原理

開發 前端
本文我們介紹 Go 方法提供的 errgroup 庫,該庫最近新增了控制并發數量的功能。我們先介紹了三種使用方式,然后通過閱讀源碼,分析其實現原理。

?1.介紹

在 Go 語言中,我們可以使用 errgroup? 庫處理 goroutine 中的錯誤。

errgroup 庫最近更新了,新增支持限制并發數量的功能。

本文我們介紹 errgroup 庫的使用方式和實現原理。

2.使用方式

errgroup 庫使用非常簡單,我們通過三個簡單示例代碼,分別介紹三種使用方式。

基礎使用

func main() {
eg := errgroup.Group{}
eg.Go(func() error {
fmt.Println("go1")
return nil
})
eg.Go(func() error {
fmt.Println("go2")
err := errors.New("go2 err")
return err
})
err := eg.Wait()
if err != nil {
fmt.Println("err =", err)
}
}

閱讀上面這段代碼,我們使用 errgroup? 庫的 Go()? 方法啟動兩個 goroutine?,分別模擬錯誤 goroutine? 和正常 goroutine。

然后,使用 errgroup? 庫的 Wait()? 方法判斷是否有 goroutine 返回錯誤信息。

附加 cancel 功能

func main() {
eg, ctx := errgroup.WithContext(context.Background())
eg.Go(func() error {
time.Sleep(1 * time.Second)
select {
case <-ctx.Done():
fmt.Println("go1 cancel, err = ", ctx.Err())
default:
fmt.Println("go1 run")
}
return nil
})
eg.Go(func() error {
err := errors.New("go2 err")
return err
})
err := eg.Wait()
if err != nil {
fmt.Println("err =", err)
}
}

閱讀上面這段代碼,我們使用 errgroup? 庫的 WithContext()? 函數,可以附加 cancel 功能。

我們在第一個使用 Go()? 方法啟動的協程函數中,使用 select ... case ... default 監聽其他協程是否返回錯誤并做出相應的邏輯處理。

限制并發數量

func main() {
eg := errgroup.Group{}
eg.SetLimit(2)
eg.TryGo(func() error {
fmt.Println("go1 run")
return nil
})
eg.TryGo(func() error {
err := errors.New("go2 err")
return err
})
eg.TryGo(func() error {
fmt.Println("go3 run")
return nil
})
err := eg.Wait()
if err != nil {
fmt.Println("err =", err)
}
}

閱讀上面這段代碼,我們使用 errgroup 庫新增的限制并發數量的功能。

首先,使用 SetLimit()? 方法設置并發數量,然后使用 TryGo()? 方法替換 Go() 方法。

3.實現原理

我們通過閱讀 errgroup? 庫的源碼,簡單介紹 errgroup 的實現原理。

我們先閱讀 Group 結構體的源碼。

type Group struct {
cancel func()

wg sync.WaitGroup

sem chan token

errOnce sync.Once
err error
}

在源碼中,我們可以發現 Group? 結構體包含的 5 個字段,其中 sem 字段是最近為了實現限制并發數量功能而新增的。

通過 Group? 結構體的字段,我們可以看出 errgroup? 實際上是對 sync? 和 context 的封裝。

其中,cancel? 是使用 context? 的 cancel? 方法;wg? 是使用 sync.WairGroup? 的相關方法;sem? 是通過 channel? 實現控制并發數量;errOnce? 是使用 sync.Once? 的特性,只保存第一個返回的 goroutine? 錯誤;err? 是 goroutine 返回的錯誤。

func (g *Group) Go(f func() error) {
if g.sem != nil {
g.sem <- token{}
}

g.wg.Add(1)
go func() {
defer g.done()

if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel()
}
})
}
}()
}

我們閱讀 errgroup? 庫的 Go()? 方法,首先,通過判斷 g.sem? 的值是否是 nil?,如果 g.sem? 的值不是 nil?,說明已設置并發數量,就通過向 g.sem? 中發送一個空結構體 token{},來搶占資源。

如果搶到資源,就啟動一個 goroutine?,否則,就阻塞,等待其他正在執行的 goroutine 釋放一個資源。

細心的讀者可能已經發現,Go()? 方法除了開頭新增判斷 g.sem? 的值是否為 nil? 的邏輯代碼之外,defer? 也發生了變化,由之前的直接調用 sync.WaitGroup? 的 Done()? 方法,改為調用 errgroup? 庫新增的 done() 方法。

done() 方法源碼:

func (g *Group) done() {
if g.sem != nil {
<-g.sem
}
g.wg.Done()
}

通過閱讀 done()? 方法的源碼,我們可以發現,在調用 sync.WaitGroup? 的 Done()? 方法之前,先判斷 g.sem? 的值是否是 nil?,如果不是 nil,則釋放資源。

我們再閱讀 Wait() 方法的源碼:

func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}

通過閱讀 Wait()? 方法的源碼,我們可以發現它實際上是封裝 sync.WaitGroup? 的 Wait()? 方法,和 context? 包的 cancel?,并且返回所有運行的 goroutine 中第一個返回的錯誤。

最后,我們閱讀新增控制并發數量的功能 TryGo()? 方法和 SetLimit() 方法的源碼:

func (g *Group) TryGo(f func() error) bool {
if g.sem != nil {
select {
case g.sem <- token{}:
// Note: this allows barging iff channels in general allow barging.
default:
return false
}
}

g.wg.Add(1)
go func() {
defer g.done()

if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel()
}
})
}
}()
return true
}

通過閱讀 TryGo()? 方法的源碼,我們可以發現,它和 Go()? 方法的區別就是在處理 g.sem 的值上,使用的邏輯不同。

TryGo()? 方法在處理 g.sem? 的值時,使用 select ... case ... default? 語句,先嘗試一次搶占資源,當無法搶到資源時,不再阻塞,而是直接返回 false,表示執行失敗。

SetLimit() 方法的源碼:

func (g *Group) SetLimit(n int) {
if n < 0 {
g.sem = nil
return
}
if len(g.sem) != 0 {
panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
}
g.sem = make(chan token, n)
}

通過閱讀 SetLimit()? 方法的源碼,我們可以看出當入參 n? 的值小于 0? 時,直接給 g.sem? 賦值為 nil,表示不限制并發數量。

在調用 SetLimit()? 方法時,g.sem? 必須是一個空通道,否則程序會 panic。

除去 SetLimit()? 方法的判斷邏輯代碼,實際上 SetLimit()? 方法就是創建一個大小為 n? 的有緩沖 channel。

SetLimit()? 和 TryGo() 通常一起使用。

4.總結

本文我們介紹 Go 方法提供的 errgroup 庫,該庫最近新增了控制并發數量的功能。

我們先介紹了三種使用方式,然后通過閱讀源碼,分析其實現原理。

責任編輯:武曉燕 來源: Golang語言開發棧
相關推薦

2022-10-17 00:07:55

Go語言標準庫

2022-05-06 09:22:25

Go泛型

2014-04-24 10:48:27

Go語言基礎實現

2023-02-13 00:24:37

Go語言日志庫

2024-02-06 17:57:06

Go語言任務

2020-08-12 08:56:30

代碼凱撒密碼函數

2023-12-11 07:33:05

Go語言字符技巧

2024-11-04 08:16:08

Go語言Web 框架

2024-10-16 09:57:52

空結構體map屬性

2019-11-12 11:15:39

setTimeout前端代碼

2024-03-25 07:22:50

GolangMySQL數據庫

2023-10-09 07:14:42

panicGo語言

2022-02-09 16:02:26

Go 語言ArraySlice

2010-09-15 15:48:09

CSS Hack

2023-04-18 08:27:16

日志級別日志包

2014-12-26 09:52:08

Go

2021-10-18 10:53:26

Go 代碼技術

2024-04-26 09:04:13

2019-08-28 09:04:02

Go語言Python操作系統

2022-05-09 10:36:05

PythonPyScript開發者
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黄网在线观看 | 一级毛片免费看 | 国产一区二区三区在线 | 国产精品高 | 亚洲天堂一区 | 久久精品视频9 | av片在线播放 | 亚洲一二三区在线观看 | 中文字幕一区二区三区精彩视频 | 欧美色成人 | 欧美成人精品一区二区男人看 | 精品视频在线免费观看 | 九九久久国产 | 一区二区免费在线观看 | 欧美性极品xxxx做受 | 欧美男人天堂 | 亚洲综合五月天婷婷 | 蜜臀网站| 国产美女视频一区 | 日韩一区二区三区在线播放 | 天天久久 | 欧美不卡在线 | 欧美成人精品一区 | 欧美日韩网站 | 91看国产| 黄色av大片| 人人人人干 | 中文字幕一区二区三区日韩精品 | 欧美久久精品一级c片 | 欧美日韩福利视频 | 日韩中文字幕免费在线观看 | 国产精品久久久久一区二区三区 | 中文字幕不卡在线观看 | 亚洲黄色在线免费观看 | 亚洲欧美日韩精品久久亚洲区 | 粉嫩粉嫩芽的虎白女18在线视频 | 久久新| 国产成人99| 久久久久久国产 | 成人综合视频在线 | av二区三区 |