1. 程式人生 > >Golang 入門系列(十七)幾個常見的併發模型——生產者消費者模型

Golang 入門系列(十七)幾個常見的併發模型——生產者消費者模型

前面已經講過很多Golang系列知識,包括併發,鎖等內容,感興趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,

接下來講幾個golang常見的併發模型,今天先說說生產者消費者模型。

 

生產者消費者模型

 

生產者:傳送資料端

消費者:接收資料端

緩衝區:
  1. 解耦(降低生產者和消費者之間耦合度)

  2. 併發(生產者消費者數量不對等時,能保持正常通訊)

  3. 快取(生產者和消費者 資料處理速度不一致時,暫存資料)

 

如何實現?

生產者消費者模型是非常常見的併發模型,而且golang提供了chan型別,可以很方便的實現。

根據 golang的官方文件,使用chan就可以實現生產者和消費者之間的資料和狀態同步。

  • 通過chan在生產者和消費者之間傳遞資料(ch)和同步狀態(done);
  • chan作為引數傳遞時是引用傳遞,不需要使用指標;
  • chan是協程安全的,多個goroutine之間不需要鎖;
  • chan的close事件可以被recv獲取,close事件一定在正常資料讀完之後,機制類似於read到EOF;
示例程式碼:

package main

import "fmt"

func Producer(ch chan int) {
  for i := 1; i <= 10; i++ {
    ch <- i
  }
  close(ch)
}

func Consumer(id int, ch chan int, done chan bool) {
  for {
    value, ok := <-ch
    if ok {
      fmt.Printf("id: %d, recv: %d\n", id, value)
    } else {
      fmt.Printf("id: %d, closed\n", id)
      break
    }
  }
  done <- true
}

func main() {
  ch := make(chan int, 3)

  coNum := 2
  done := make(chan bool, coNum)
  for i := 1; i <= coNum; i++ {
    go Consumer(i, ch, done)
  }

  go Producer(ch)

  for i := 1; i <= coNum; i++ {
    <-done
  }
}


執行結果:
id: 2, recv: 1
id: 1, recv: 2
id: 1, recv: 4
id: 1, recv: 5
id: 1, recv: 6
id: 2, recv: 3
id: 2, recv: 8
id: 2, recv: 9
id: 2, recv: 10
id: 2, closed
id: 1, recv: 7
id: 1, closed

 

簡單說明:

1、首先建立一個雙向的channel,

2.、然後開啟一個新的goroutine,把雙向通道作為引數傳遞到producer方法中,同時轉成只寫通道。子協程開始執行迴圈,向只寫通道中新增資料,這就是生產者。

3、主協程,直接呼叫consumer方法,該方法將雙向通道轉成只讀通道,通過迴圈每次從通道中讀取資料,這就是消費者。


注意:channel作為引數傳遞,是引用傳遞。

 

緩衝區的好處

1:解耦
假設生產者和消費者分別是兩個類。如果讓生產者直接呼叫消費者的某個方法,那麼生產者對於消費者就會產生依賴(也就是耦合)。將來如果消費者的程式碼發生變化,可能會直接影響到生產者。而如果兩者都依賴於某個緩衝區,兩者之間不直接依賴,耦合度也就相應降低了。

2:處理併發
生產者直接呼叫消費者的某個方法,還有另一個弊端。由於函式呼叫是同步的(或者叫阻塞的),在消費者的方法沒有返回之前,生產者只好一直等在那邊。萬一消費者處理資料很慢,生產者只能無端浪費時間。
使用了生產者/消費者模式之後,生產者和消費者可以是兩個獨立的併發主體。生產者把製造出來的資料往緩衝區一丟,就可以再去生產下一個資料。基本上不用依賴消費者的處理速度。
其實最當初這個生產者消費者模式,主要就是用來處理併發問題的。

3:快取
如果生產者製造資料的速度時快時慢,緩衝區的好處就體現出來了。當資料製造快的時候,消費者來不及處理,未處理的資料可以暫時存在緩衝區中。等生產者的製造速度慢下來,消費者再慢慢處理掉。

 

最後

以上,就把golang生產者消費者模型簡單介紹完了,希望能對大家有所幫助。