學習 Go併發模型

奇蹟師發表於2021-08-06

前言

  • 簡化併發開發
  • 以下程式碼內容均來自大彬大佬

1.簡單例子

  • 將陣列內的資料轉變為他們的平方
  • 分解以上過程為三個步驟
    • 生產資訊 producer(),遍歷切片
    • 處理資訊 square(),計算平方
    • 消費資訊 main(),消費

1.生產資訊

func producer(nums ...int) <-chan int {
    // 建立帶緩衝通道
    out := make(chan int,10)
    // 通過協程將資料儲存到通道中
    go func(){
        defer close(out) //最後關閉通道
        for _,num := range nums {
            out <- num
        }
    }()
    return out
}

2.處理資訊

func square(inCh <-chan int) <-chan int {
    out := make(chan int,10)
    go func(){
        defer cloes(out)
        for n := range inCh {
            out <- n*n
        }
    }()
    return out
}

3.消費資訊

func main() {
    // 先將資料拆分放入通道
    in := producer(1,2,3,4)
    // 處理資料
    ch := square(in)
    // 消費資料
    for ret := range ch {
    fmt.Printf("%3d",ret)
    }
}

扇形模型優化 FAN-IN 與 FAN-OUT

  • FAN-OUT : 多個 goruntine 從同一個通道讀取資料,直到該通道關閉
  • FAN-IN :1個 goruntine 從多個通道讀取資料,直到該通道關閉

1. FAN-OUT 和 FAN-IN 實踐

1.生產者producer() 和 訊息處理square()不變

func producer(nums ...int) <-chan int {
    // 建立帶緩衝通道
    out := make(chan int,10)
    // 通過協程將資料儲存到通道中
    go func(){
        defer close(out) //最後關閉通道
        for _,num := range nums {
            out <- num
        }
    }()
    return out
}

func square(inCh <-chan int) <-chan int {
    out := make(chan int,10)
    go func(){
        defer cloes(out)
        for n := range inCh {
            out <- n*n
        }
    }()
    return out
}

2. 新增merge() 用來多個square() 操作最後迴歸到一個通道消費讀取— FAN-IN

func merge(cs ...<-chan int) <-chan int {
    out := make(chan int,10)

    // 建立計時器
    var wg sync.WaitGroup
    // 將所有資料迴歸到一個通道中
    // 該通道為可操作通道
    collect := func (in chan int){
        defer wg.Done()
        for n := range in {
            out <- n
        }
    }

    wg.Add(len(cs))
    // FAN - IN
    for _,c := range cs {
        go collect(c)
    }

    // 錯誤方式:直接等待是bug,死鎖,因為merge寫了out,main卻沒有讀
    // wg.Wait()
     // close(out)

    go func(){
        wg.Wait()
        close(out)
    }()

    return out
}

3.修改main(),啟動3個square(),一個生產者producer()被多個square()讀取 — FAN-OUT

func main() {
    in := producer(1,2,3,4)

    // FAN-OUT  這個時候開啟了協程
    c1 := square(in)
    c2 := square(in)
    c3 := square(in)

    // consumer
    for ret := merge(c1,c2,c3) {
        fmt.Printf("%3d",ret)
    }
}

3.優化 FAN 模式

  • 不同的場景優化不同,要依據具體的情況,解決程式的瓶頸
  • 但總的來說 不推薦用無緩衝通道,推薦用有緩衝通道

結語

  • 這是一篇學習部落格,推薦去看 原文章
  • 謝謝能看到最後
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章