Golang 教學系列 - close 與 for range 搭配 channel 觀念教學
上次的文章介紹:單雙向 channel 的差別及宣告方式,可以參考:Golang 教學系列 - channel 裡面資料什麼都能放?單雙向 channel 的差別
今天這篇文章主要介紹如何對 channel 進行關閉以及如何透過 for range
來尋訪 channel 裡面的 element。如果想要詳細的介紹可以參考我的教學影片:Golang 教學系列 - close 與 for range 搭配 channel 觀念教學!| 肯尼攻城獅
關閉 channel
直接上程式碼:
1 | package main |
上面我宣告了一個 channel 變數,裡面可以放的型態是 int
。然後透過一個 keyword:close
可以將 channel 關閉。
-
一旦 channel 關閉之後就不能再往 channel 裡面送資料了。
-
如果持續送資料會出現 panic 的錯誤,所以如果跑上面的程式就會出現以下的錯誤:
1
panic: send on closed channel
-
雖然 channel 被關閉但仍是可以從裡面取出資料。
例如:
1
2
3
4
5
6
7
8
9
10
11
12package main
import "fmt"
func main() {
intChan := make(chan int, 3)
intChan <- 1
intChan <- 2
close(intChan)
a := <- intChan
fmt.Println(a)
}這樣依舊可以從 channel 裡面取出值並將值賦予給 a。
for loop 從 channel 裡面取資料
直接上程式碼:
1 | package main |
這個範例是只有主程式來對 channel for loop 取出資料,最直覺的方式當然就是 channel 裡面有多少元素我就取幾次的作法。所以在第一個 for loop 我將元素放入 channel 放到滿,再透過第二個 for loop 將全部元素取出來
缺點在於:這樣之所以沒問題是因為我是把 channel 容量塞到滿,但很多情況 channel 不會被塞滿,然後我就想取出來了,可是我怎麼知道我 for loop 要取幾次?
邊送邊從 channel 取資料
有的情境是 channel 再被送資料的同時,我有另外一個 goroutine 可以就取出來,或是另外一種情境是這個 channel 送完資料就會關閉了,我另外一邊的 goroutine 要把資料取完。
透過 for range 與 close 的搭配就可以。
請看程式碼:
1 | package main |
這邊先不用 close,單純使用 for range,透過 for range 可以簡單的尋訪 channel 裡面的元素,會將 channel 裡面元素的值賦予給 i。
但是運行該程式還是會出現 deadlock 錯誤:
1 | fetal error: all goroutines are asleep - deadlock! |
因為這個 for range 會一直不斷從 channel 裡面取出資料,如果 channel 被取到沒有資料就會被 block 住,直到 channel 裡面有資料為止,因此造成 deadlock。
這就是為什麼如果想要避免這樣的錯誤就可以使用 close channel 的動作。透過 close channel,for range 取出所有資料後就會自動往下執行,而不會被 block 住。
再看一個 for range 例子
1 | package main |
主程式主要是:
- 宣告 channel 為容量為 1
- 開啟一個 goroutine 來產生數字丟進 channel
- 使用 for range 對 channel 裡面取出資料並且印出
generateNumbers 主要作用是:
用一個 for loop 去隨機產生 1~10 的數字,然後將數字丟進 channel 裡面,而這個 for loop 只會運行 6 次。當判斷 count 是 6 的時候則將 channel 關閉並 break for loop。
這樣的寫法可以知道由於一個 goroutine 再放資料,另一個 goroutine 再取資料,而當 for range 這邊取資料如果比較快的話這邊就會持續 block 住,直到 channel 裡面還有資料可以取為止,或是如果 channel 被關閉的話就代表資料已經取完可以離開這個 for range。
for range 搭配 bool,value 取值的方式
來看程式碼:
1 | package main |
這邊可以看到主程式這邊從 channel 取出資料的時候可以有兩個回傳值,第二個值是 bool 型態是代表說 channel 裡面取不取的到資料。這個取不取得到資料看的是 channel 有沒有被關閉,只有當 channel 被關閉的時候這邊的 bool 值才會回傳 false,所以如果單純取資料的話如果取不到的話是會一直被 block 住的。
總結
今天主要介紹如何透過 for range 與 close 來尋訪 channel 裡面的資料。也是介紹一種方式來讓 main goroutine 等待其他 goroutine 的方式。下一篇文章會介紹 channel 常見的應用範例。