Golang Channel詳細解析

引言

Goroutine 和 Channel 是 Go 語言併發編程的兩大基石。Goroutine 用於執行併發任務,Channel 用於 goroutine 之間的同步、通信。

在Golang的併發哲學裡,有一句非常著名的話:

Do not communicate by sharing memory; instead, share memory by communicating.

意思是:不要通過共享內存來通信,而要通過通信來實現內存共享,它依賴CSP(Communication Sequence Process) 模型,簡稱通信順序進程。

Go提倡使用通信的方法代替共享內存,當一個Goroutine需要和其他Goroutine資源共享時,Channel就會在他們之間架起一座橋樑,並提供確保安全同步的機制。

Golang Channel詳細解析

Channel本質上還是一個隊列,遵循FIFO(First In-First Out)原則,

創建通道

創建通道需要用到關鍵字 make ,格式如下:

<code>通道實例 := make(chan 數據類型)/<code>
  • 數據類型:通道內傳輸的元素類型。
  • 通道實例:通過make創建的通道句柄。

使用通道

通道創建後,就可以使用通道進行發送和接收操作。

寫入

通道的寫入使用特殊的操作符

<code>通道變量 /<code>

(可以簡單理解為箭頭方向為傳遞的值最終去向)

<code>// 創建一個空接口通道
ch := make(chan interface{})
// 將0放入通道中
ch // 將hello字符串放入通道中

ch /<code>

讀取

通道的讀取同樣使用

  1. 通道的收發操作在不同的兩個 goroutine 間進行。
  2. 由於通道的數據在沒有接收方處理時,數據發送方會持續阻塞,因此通道的接收必定在另外一個 goroutine 中進行。
  3. 接收將持續阻塞直到發送方發送數據。

通道的數據接收一共有以下 4 種寫法:

1) 阻塞式接收

阻塞模式接收數據時,接收值只有一個,格式如下:

<code>data := /<code>

執行該語句時程序將會阻塞,直到接收到數據並賦值給 data 變量。

2) 非阻塞接收數據

使用非阻塞方式從通道接收數據時,語句不會發生阻塞,格式如下:

<code>data, ok := /<code>

data:表示接收到的數據。未接收到數據時,data 為通道類型的零值。

ok:表示是否接收到數據。

特點:非阻塞的通道接收方法可能造成高的 CPU 佔用,不建議這麼使用。

3) 忽略接收的數據

忽略從通道返回的任何數據,格式如下:

<code>

特點:該方法也是阻塞的,必須等到通道返回了程序才會繼續往下走。

4) 循環接收

通道的數據接收可以借用 for range 語句進行多個元素的接收操作,格式如下:

<code>for data := range ch {
// do sth.
}/<code>

通道 ch 是可以進行遍歷的,遍歷的結果就是接收到的數據。數據類型就是通道的數據類型。通過 for 遍歷獲得的變量只有一個,即上面例子中的 data。

只讀/只寫通道

一般來說,通道都是雙向的,即數據可以進入和輸出。但是,為了符合某些特殊業務場景,官方還提供了只支持讀(Read Only)或只支持寫(Write Only)的通道,格式如下:

<code>//定義只讀通道
ch_r := //定義只寫通道
ch_w := /<code>

有緩衝通道

Go語言中有緩衝的通道(buffered channel)是一種在被接收前能存儲一個或者多個值的通道。這種類型的通道並不強制要求 goroutine 之間必須同時完成發送和接收。通道會阻塞發送和接收動作的條件也會不同。只有在通道中沒有要接收的值時,接收動作才會阻塞。只有在通道沒有可用緩衝區容納被髮送的值時,發送動作才會阻塞。

有緩衝通道的定義方式如下:

<code>通道實例 := make(chan 通道類型, 緩衝大小)/<code>
  • 通道類型:和無緩衝通道用法一致,影響通道發送和接收的數據類型。
  • 緩衝大小:決定通道最多可以保存的元素數量。
  • 通道實例:被創建出的通道實例。

下面我借用以下的圖片來說明下這個通道原理

Golang Channel詳細解析

  1. 左邊的goroutine不斷的往通道中塞數據
  2. 右邊的goroutine不斷的從通道中拿數據
  3. 一個讀/一個寫,同步作業
  4. 左邊的goroutine全部數據已經放完了,但此時通道中還剩餘數據,所有右邊的goroutine還在工作

無緩衝通道

Go語言中無緩衝的通道(unbuffered channel)是指在接收前沒有能力保存任何值的通道。這種類型的通道要求發送 goroutine 和接收 goroutine 同時準備好,才能完成發送和接收操作。

無緩衝通道的定義方式如下:

<code>通道實例 := make(chan 通道類型)/<code>
  • 通道類型:和無緩衝通道用法一致,影響通道發送和接收的數據類型。
  • 緩衝大小:0
  • 通道實例:被創建出的通道實例。

為了講得更清楚一些,我也找了一張額外的圖來說明:

Golang Channel詳細解析

在第 1 步,兩個 goroutine 都到達通道,但哪個都沒有開始執行發送或者接收。在第 2 步,左側的 goroutine 將它的手伸進了通道,這模擬了向通道發送數據的行為。這時,這個 goroutine 會在通道中被鎖住,直到交換完成。在第 3 步,右側的 goroutine 將它的手放入通道,這模擬了從通道里接收數據。這個 goroutine 一樣也會在通道中被鎖住,直到交換完成。在第 4 步和第 5 步,進行交換,並最終在第 6 步,兩個 goroutine 都將它們的手從通道里拿出來,這模擬了被鎖住的 goroutine 得到釋放。兩個 goroutine 現在都可以去做別的事情了。

總結

GoLang的通道是支撐併發系統穩定高效運行的重要工具,只有充分了解了它,才能在業務開發和問題排查中找到最關鍵的方案。知己知彼,百戰不殆。


分享到:


相關文章: