欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go中groutine通信與context控制實例詳解

 更新時間:2022年02月11日 15:54:56   作者:DogTwo  
隨著context包的引入,標準庫中很多接口因此加上了context參數(shù),下面這篇文章主要給大家介紹了關于Go中groutine通信與context控制的相關資料,需要的朋友可以參考下

需求背景:

項目中需要定期執(zhí)行任務A來做一些輔助的工作,A的執(zhí)行需要在超時時間內完成,如果本次執(zhí)行超時了,那就不對本次的執(zhí)行結果進行處理(即放棄這次執(zhí)行)。同時A又依賴B,C兩個子任務的執(zhí)行結果。B, C之間相互獨立,可以并行的執(zhí)行。但無論B,C哪一個執(zhí)行失敗或超時都會導致本次任務執(zhí)行失敗。

Groutine的并發(fā)控制:

go中對于groutine的并發(fā)控制有三種解決方案:

  • 通過channel控制。

    父groutine中聲明無buffer的chan切片,向要開啟的子groutine中傳入切片中的一個chan

    子groutine執(zhí)行完成后向這個chan中寫入數(shù)據(jù)(可以是和父groutine通信的也可以不是)

    父groutine遍歷所有chan并執(zhí)行 <-chan 操作, 利用無buffer的channel只有讀寫同時準備好才能執(zhí)行的特性進行控

  • WaitGroup控制。

    通過sync.Waitgroup, 每開啟一個子groutine就執(zhí)行 wg.Add(1), 子groutine內部執(zhí)行wg.Done(), 父groutine通過wg.Wait()等待所有子協(xié)程

  • Context控制。

    waitGroup和Context應該是Go中較為常用的兩種并發(fā)控制。相較而言,context對于派生groutine有更強大的控制力,可以控制多級樹狀分布的groutine。

    當然waitGroup的子groutine也可以再開啟新的waitGroup并且等待多個孫groutine, 但是不如context的控制更加方便.

Context:

context包提供了四個方法創(chuàng)建不同類型的context

  • WitchCancel()
  • WithDeadline()
  • WithTimeout()
  • WithValue()

WithValue()主要用于通過context傳遞一些上下文消息,不在本次討論中。WithTimeout和WithDeadLine幾乎是一致的。但無論哪種,控制groutine都需要使用ctx.Done()方法. Done() 方法返回一個 "只讀"的chan <-chan struct{}, 需要編寫代碼監(jiān)聽這個chan,一旦收到它的消息就說明這個context應當結束了,無論是到達了超時時間還是在某個地方主動cancel()了方法。

看看代碼:

var ch1 chan int
var ch2 chan int
<br>// 任務A, 通過最外層的for來控制定期執(zhí)行
func TestMe(t *testing.T) {
    ch1 = make(chan int, 0)
    ch2 = make(chan int, 0)
    count := 0
    for {
        count ++
        ctx, cancel := context.WithTimeout(context.Background(), time.Second * 2)<br>                // 任務A的邏輯部分,開啟子任務B, C。<br>                // B,C通過ch1,ch2和A通信。<br>                // 同時監(jiān)聽ctx.Done,如果超時了立即結束本次任務不繼續(xù)執(zhí)行
        go func(ctx context.Context) {
            go g1(ctx, count)
            go g2(ctx, count)
            v1, v2 := -1, -1
            for v1 == -1 || v2 == -1 {
                select {
                case <- ctx.Done():
                    cancel()
                    fmt.Println("父級2超時退出,當前count值為", count, "當前時間:", time.Now())
                    return
                case v1 = <- ch1:
                case v2 = <- ch2:
                }
            }
            fmt.Println("正常執(zhí)行完成退出, 開啟下次循環(huán),當前count值為:", count, "當前 v1: ", v1, "當前 v2: ", v2)
        }(ctx)<br>                // 任務A監(jiān)控ctx是否到達timeOUT,timeout就終止本次執(zhí)行
        select {
        case <- ctx.Done():
            fmt.Println("父級1超時退出,當前count值為", count, "當前時間:", time.Now())
        }
        time.Sleep(time.Second * 3)
    }
}
<br>// 改進后的任務B,即使計算出了結果,也不會再向ch1寫數(shù)據(jù)了,不會造成臟數(shù)據(jù)
func g1 (ctx context.Context, num int) {
    fmt.Println("g1 num", num, "time", time.Now())
    select {
    case <-ctx.Done():
        fmt.Println("子級 g1關閉, 不向channel中寫數(shù)據(jù)")
        return
    default:
        ch1 <- num
    }
}
<br>// 改進前的任務C
func g2 (ctx context.Context, num int) {
    fmt.Println("g2 num", num, "time", time.Now())
    ch2 <- num

基于上述代碼,子任務B, C的處理其實有一次較大的變動。一開始B,C都是類似于子任務C,即g2的這種寫法。

這種寫法在執(zhí)行完成后就把自身的結果交給channel, 父groutine通過channel來讀取數(shù)據(jù),正常情況下也能工作。但異常情況下,如子任務B執(zhí)行完成,子任務C(即g2)因為網絡通信等原因執(zhí)行了5s(超過context的最大時長), 就會出現(xiàn)比較嚴重的問題。到達超時時間后,A檢測到了超時就自動結束了本次任務,但g2還在執(zhí)行過程中。g2執(zhí)行完成后向ch2寫數(shù)據(jù)阻塞了(因為A已關閉,沒有讀取ch2的groutine)。下一個循環(huán)中A再次開啟讀取ch1與ch2, 實際上讀取ch1是當次的結果,ch2是上次任務中g2返回的結果,導致兩處依賴的數(shù)據(jù)源不一致。

模擬上述情況,將g2做了一些改動如下:

// 在第3次任務重等待3s, 使得它超時<br>func g2 (ctx context.Context, num int) {
    if num == 3 {
        time.Sleep(time.Second * 3)
    }
    fmt.Println("g2 num", num, "time", time.Now())
    ch2 <- num
}

實際上,如果想要通過context控制groutine, 一定要監(jiān)控Done()方法。如g1所示。相同情況下A超時退出,C仍在執(zhí)行。C執(zhí)行完成后先檢測Context是否已退出,如果已退出就不再向ch2中寫入本次的數(shù)據(jù)了。(拋磚引玉了,也可能有更好的寫法,希望大佬不吝賜教)

將g2改成和g1類似的寫法后測試結果如下:

func g2 (ctx context.Context, num int) {
    if num == 3 {
        time.Sleep(time.Second * 10)
        fmt.Println("這次g2 超時,應當g1, g2都不返回")
    }
    fmt.Println("g2 num", num, "time", time.Now())
    select {
    case <-ctx.Done():
        fmt.Println("子級 g2關閉, 不向channel中寫數(shù)據(jù)")
        return
    default:
        ch2 <- num
    }
}

總結

到此這篇關于Go中groutine通信與context控制的文章就介紹到這了,更多相關Go groutine通信與context控制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • go-zero源碼閱讀之布隆過濾器實現(xiàn)代碼

    go-zero源碼閱讀之布隆過濾器實現(xiàn)代碼

    布隆過濾器可以用于檢索一個元素是否在一個集合中。它的優(yōu)點是空間效率和查詢時間都比一般的算法要好的多,缺點是有一定的誤識別率和刪除困難,這篇文章主要介紹了go-zero源碼閱讀-布隆過濾器,需要的朋友可以參考下
    2023-02-02
  • 一文總結Go語言切片核心知識點和坑

    一文總結Go語言切片核心知識點和坑

    都說Go的切片用起來絲滑得很,Java中的List怎么用,切片就怎么用,作為曾經的Java選手,因為切片的使用不得當,喜提缺陷若干,本文就給大家總結一下Go語言切片核心知識點和坑,需要的朋友可以參考下
    2023-06-06
  • Go語言流程控制語句

    Go語言流程控制語句

    這篇文章介紹了Go語言流程控制語句的用法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • go1.8之安裝配置具體步驟

    go1.8之安裝配置具體步驟

    下面小編就為大家?guī)硪黄猤o1.8之安裝配置具體步驟。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • 詳解Golang如何實現(xiàn)節(jié)假日不打擾用戶

    詳解Golang如何實現(xiàn)節(jié)假日不打擾用戶

    這篇文章主要為大家介紹了Golang如何實現(xiàn)節(jié)假日不打擾用戶過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • 解析golang 標準庫template的代碼生成方法

    解析golang 標準庫template的代碼生成方法

    這個項目的自動生成代碼都是基于 golang 的標準庫 template 的,所以這篇文章也算是對使用 template 庫的一次總結,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2021-11-11
  • Go1.18新特性工作區(qū)模糊測試及泛型的使用詳解

    Go1.18新特性工作區(qū)模糊測試及泛型的使用詳解

    這篇文章主要為大家介紹了Go?1.18新特性中的工作區(qū)?模糊測試?泛型使用進行詳細講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • golang goroutine順序輸出方式

    golang goroutine順序輸出方式

    這篇文章主要介紹了golang goroutine順序輸出方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go基礎教程系列之數(shù)據(jù)類型詳細說明

    Go基礎教程系列之數(shù)據(jù)類型詳細說明

    這篇文章主要介紹了Go基礎教程系列之數(shù)據(jù)類型詳細說明,需要的朋友可以參考下
    2022-04-04
  • Golang中的同步工具sync.WaitGroup詳解

    Golang中的同步工具sync.WaitGroup詳解

    這篇文章主要詳細為大家介紹了Golang中的同步工具sync.WaitGroup,文中有詳細的代碼示例,具有很好的參考價值,希望對大家有所幫助,一起跟隨小編過來看看吧
    2023-05-05

最新評論