快速喫透 Golang Channels 使用技巧

Golang 可以通過啓動 goroutines 來併發執行任務。它們可以通過一種名爲 "通道" 的通信媒介相互通信。話不多說,下面我列舉了幾種不同情況下 channel 的使用以及其適用條件,最後總結出一張 channel table,能夠幫助你快速喫透 channel 的所有要點。Let't go!

Nil Channels

如果你像創建普通變量一樣創建一個通道,通道將被初始化爲零值。這裏要提到的另一個重要細節是,通道的發送方和接收方都必須做好通信準備,否則我們將收到一個死鎖錯誤。這可以通過緩衝通道來避免,我們將在後面討論。

// NOTE: Deadlock
func nilChannel() {
 var ch chan int

// creating a goroutine and sending value on the channel
 go func() {
  ch <- 1
 }()

 fmt.Println(<-ch)
}

如果你在 main 中調用這個函數,你會得到以下錯誤和一些其他信息。

fatal error: all goroutines are asleep - deadlock!

這是因爲我們試圖在往 nil channel 上發送一個值,這是堅決不允許的操作。

Empty Channel

在以下代碼中,我們將收到同樣的死鎖錯誤,因爲我們正試圖在 channel receiver 尚未準備好的通道上發送值。從下面的代碼中可以看到,在接收器創建之前,值就已經在通道上發送了。

// NOTE: Deadlock
func emptyChannel() {
 var ch = make(chan int)

 ch <- 1

 fmt.Println(<-ch)
}

UN Buffered Channel

錯誤的代碼已經夠多了。這次讓我給出一個有效的代碼。在下面的代碼中,我們編寫了與上一個示例相同的代碼,但不是創建一個 nil 通道,而是使用 make 函數創建了一個用默認值初始化的通道。不過,如果在這種情況下沒有接收器,就會錯過結果,但不會產生死鎖錯誤。

// NOTE: send single value through goroutine
func simpleChannel() {
 var ch = make(chan int)

 go func() {
  ch <- 1
 }()

// NOTE: if you comment this line. You will not be able to receive the result but code will not crash
 fmt.Println(<-ch)
}

Channel 方向

默認情況下通道是雙向的。下面的代碼顯示了一個只能用於發送值的通道。如果我們試圖從中獲取值,就會出錯。

func uniDirectionalChannel() {
// Bidirectional [outside goroutine]
 var ch = make(chan int)

 go func(ch chan<- int) {
 // unidirectinal [within goroutine]
  ch <- 1
 }(ch)

 fmt.Println(<-ch)
}

Buffered Channel

這些通道可以像數組一樣容納多個值,因此在非緩衝通道中,如果我們嘗試在沒有接收器的情況下向其寫入數據,就會出錯,但在緩衝通道中,我們可以向其寫入數據,直到緩衝區滿爲止。當緩衝區已滿時,如果我們嘗試向其中寫入新值,就會出錯。如果我們這裏不註釋該函數的最後一行,就會出現死鎖錯誤,因爲這裏將從空通道讀取數據。

// using buffered channel
func bufferdChannelWithoutLoop() {
 var ch = make(chan int, 2)

 ch <- 1
 ch <- 2

 fmt.Println(<-ch)
 fmt.Println(<-ch)
 // fmt.Println(<-ch) // NOTE: Deadlock, Reading from empty channel
}

從通道讀取數值還有另一種方法,即使用循環。在前面的示例中,我們逐個讀取數值,但我們也可以通過循環讀取接收通道中的數值,因此每當通道中發送一個新數值時,循環就會迭代,執行完正文中的代碼後,就會等待下一個數值。如果接收器試圖讀取,但通道上已沒有其他值,則會出現同樣的死鎖錯誤。

開發人員的職責是在通道使用後將其關閉,因爲如果接收器試圖讀取已經關閉的通道,就會出現上述死鎖問題。

Channels Table

我們已經討論了通道的所有情況,但怎麼才能快速記住它們呢?別緊張😎,有我在,下表可以作爲快速指南,對照着錶盤一下我們寫的 channel 就能避免出現死鎖,並顯示它們在不同情況下的行爲。

另外,推薦一個非常詳細的講解 channel 的視頻:https://www.youtube.com/watch?v=fCkxKGd6CVQ

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3MFTD5_FG80fSyHSeZhMuw