Golang 教學系列 - 何謂 Channel? 先從宣告 Channel 開始學起!
上次講了 WaitGroup 如何應用在 Goroutine 上,以及常見的坑:Golang 教學系列 - WaitGroup 常見的坑以及應用介紹
今天這篇文章要介紹的是何謂 Channel?先從宣告 Channel 開始學起!
如果想要知道更詳細的講解可以參考我的影片:Golang 教學系列 - 何謂 Channel? 先從宣告 Channel 開始學起!| 肯尼攻城獅
Channel 是什麼
- Channel 是一種型態一種資料型態
- Channel 是管道的意思,在裡面可以放一些資料,而裡面的資料可以透過 Goroutine 放進去
Channel 宣告方式
宣告 channel
1 | package main |
- 宣告的時候需要加上
chan
keyword 再加上 管道裡面要放怎樣型態的 element,所以上面的例子來看:var intChan chan int
代表了宣告了一個叫做 intChan 的變數,這個變數是 chan 型態,裡面可以放 int 型態的 element - 如果只有單純宣告就輸出的話,會輸出成
nil
放入元素到 channel
錯誤用法
如果宣告完 channel 就直接往裡面放元素會導致以下錯誤:
1 | package main |
1 | fetal error: all goroutines are asleep - deadlock! |
之所以會出現 deadlock 錯誤是因為如果只有單純宣告 channel,並沒有初始化要給該 channel 多少容量的話,元素會放不進去,放不進去的話這邊就會一直被 block 住,也就是這個地方被 block 住:intChan <- 10
所以 nil channel 無法往裡面放 element。
正確用法
應該是要宣告的時候同時也要進行初始化容量的動作:
1 | package main |
- 透過 make 給予管道內的容量為 1,代表可以放一個元素
- 透過
<-
來放進元素,透過<-
放在 channel 變數就代表取出管道內的元素 - 因此這邊就會輸出為 10
可以透過 len()跟cap()
來做驗證,是否真的從管道內取出元素
1 | package main |
這邊 len () 取得的是管道內的元素的數量,所以一開始會是 0,當放進元素後會改成 1,再取出來,之後又會變成 0。
運行之後輸出如下:
1 | 0 |
該注意的點
-
channel 內的容量一旦滿了之後,再往裡面放元素會造成 block 的動作,除非有取出的動作才不會被 block 住,如下範例:
1
2
3
4
5
6
7
8
9package main
func main() {
var intChen = make(chan int, 3)
intChen <- 1
intChen <- 2
intChen <- 3
intChen <- 4
}intChan
這邊會被 block 住而無法往裡面丟 4所以可以改成取出來:
1
2
3
4
5
6
7
8
9
10package main
func main() {
var intChen = make(chan int, 3)
intChen <- 1
intChen <- 2
intChen <- 3
<- intChan
intChen <- 4
}這樣就可以往裡面放 4,然後取出來的元素可以給一個變數或是也不給,不給的話就像上面那樣單純取出來就可以了
這邊可以注意的是,取出來的順序是先進先出,所以這邊取出來是取出 1。可以輸出來做驗證:
1
2
3
4
5
6
7
8
9
10
11package main
func main() {
var intChen = make(chan int, 3)
intChen <- 1
intChen <- 2
intChen <- 3
b := <- intChan
intChen <- 4
fmt.Println(b)
}會輸出為 1。
0 個容量的 channel
如果這樣宣告:
1 | package main |
-
stringChan 跟 stringChan2 都是 0 個容量的意思,如果第二個參數省略不寫預設就是 0。
-
0 個容量代表既不能往 channel 丟資料也不能從 channel 裡面取資料
-
假設有兩個 Goroutine 要透過 0 個容量 channel 溝通的話,可以這樣寫,就不會造成 deadlock:
1
2
3
4
5
6
7
8
9
10
11
12package main
import "fmt"
func main() {
stringChan := make(chan string)
go func() {
stringChan <- "hello world"
}()
fmt.Println(<- stringChan)
}這樣不會被 block 住是因為,同時有兩個 Goroutine 往裡面取資料跟放資料這樣不會造成 deadlock。
而且這樣也可以拿到資料。
總結
這篇文章介紹了 channel 的宣告方式及放入跟取出元素的方式,同時也介紹會發生 deadlock 的情況以及如何避免。下篇文章會介紹 channel 裡面還能放怎樣的資料,以及單雙向的 channel 的差別?以這篇文章的例子都是雙向的 channel 也就是可以往裡面丟資料以及取資料。
最後最後!請聽我一言!
如果你還沒有註冊 Like Coin,你可以透過我的邀請註冊連結來免費註冊,註冊完後就可以在文章最下方幫我按下 Like 按鈕,而 Like 最多可以點五次,如此一來你不用付出任何一塊錢,就能給我寫這篇文章最大的回饋!