Go 只讀通道/只寫通道/有緩衝通道/無緩衝通道【深度解析】

引言

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就會在他們之間架起一座橋樑,並提供確保安全同步的機制。工具

圖片

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

建立通道

建立通道須要用到關鍵字make,格式以下:3d

通道實例 := make(chan 數據類型)
  • 數據類型:通道內傳輸的元素類型。
  • 通道實例:經過make建立的通道句柄。

使用通道

通道建立後,就可使用通道進行發送和接收操做。code

寫入

通道的寫入使用特殊的操做符<-,將數據經過通道發送的格式爲:blog

通道變量 <- 值

(能夠簡單理解爲箭頭方向爲傳遞的值最終去向)接口

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

讀取

通道的讀取一樣使用<-操做符,通道接收有以下特性:隊列

  1. 通道的收發操做在不一樣的兩個 goroutine 間進行。
  2. 因爲通道的數據在沒有接收方處理時,數據發送方會持續阻塞,所以通道的接收一定在另一個 goroutine 中進行。
  3. 接收將持續阻塞直到發送方發送數據。

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

1) 阻塞式接收

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

data := <-ch

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

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

data, ok := <-ch

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

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

3) 忽略接收的數據

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

<-ch

特色:該方法也是阻塞的,必須等到通道返回了程序纔會繼續往下走。
4) 循環接收

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

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

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

通道類型

只讀/只寫通道

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

//定義只讀通道
ch_r := <-chan interface{}
//定義只寫通道
ch_w := <-chan interface{}

有緩衝通道

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

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

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

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

圖片

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

無緩衝通道

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

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

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

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

圖片

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

總結

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

相關文章
相關標籤/搜索