通道是一個參照物件,和 map 類似。map 在沒有任何外部參照時,Go語言程式在執行時(runtime)會自動對記憶體進行垃圾回收(Garbage Collection, GC)。類似的,通道也可以被垃圾回收,但是通道也可以被主動關閉。
格式
使用 close() 來關閉一個通道:
close(ch)
關閉的通道依然可以被存取,存取被關閉的通道將會發生一些問題。
給被關閉通道傳送資料將會觸發 panic
被關閉的通道不會被置為 nil。如果嘗試對已經關閉的通道進行傳送,將會觸發宕機,程式碼如下:
package main
import "fmt"
func main() {
// 建立一個整型的通道
ch := make(chan int)
// 關閉通道
close(ch)
// 列印通道的指標, 容量和長度
fmt.Printf("ptr:%p cap:%d len:%dn", ch, cap(ch), len(ch))
// 給關閉的通道傳送資料
ch <- 1
}
程式碼執行後觸發宕機:
panic: send on closed channel
程式碼說明如下:
-
第 7 行,建立一個整型通道。
-
第 10 行,關閉通道,注意 ch 不會被 close 設定為 nil,依然可以被存取。
-
第 13 行,列印已經關閉通道的指標、容量和長度。
-
第 16 行,嘗試給已經關閉的通道傳送資料。
提示觸發宕機的原因是給一個已經關閉的通道傳送資料。
從已關閉的通道接收資料時將不會發生阻塞
從已經關閉的通道接收資料或者正在接收資料時,將會接收到通道型別的零值,然後停止阻塞並返回。
操作關閉後的通道:
package main
import "fmt"
func main() {
// 建立一個整型帶兩個緩衝的通道
ch := make(chan int, 2)
// 給通道放入兩個資料
ch <- 0
ch <- 1
// 關閉緩衝
close(ch)
// 遍歷緩衝所有資料, 且多遍歷1個
for i := 0; i < cap(ch)+1; i++ {
// 從通道中取出資料
v, ok := <-ch
// 列印取出資料的狀態
fmt.Println(v, ok)
}
}
程式碼執行結果如下:
0 true
1 true
0 false
程式碼說明如下:
-
第 7 行,建立一個能儲存兩個元素的帶緩衝的通道,型別為整型。
-
第 10 行和第11行,給這個帶緩衝的通道放入兩個資料。這時,通道裝滿了。
-
第 14 行,關閉通道。此時,帶緩衝通道的資料不會被釋放,通道也沒有消失。
-
第 17 行,cap() 函數可以獲取一個物件的容量,這裡獲取的是帶緩衝通道的容量,也就是這個通道在 make 時的大小。雖然此時這個通道的元素個數和容量都是相同的,但是 cap 取出的並不是元素個數。這裡多遍歷一個元素,故意造成這個通道的超界存取。
-
第 20 行,從已關閉的通道中獲取資料,取出的資料放在 v 變數中,型別為 int。ok 變數的結果表示資料是否獲取成功。
-
第 23 行,將 v 和 ok 變數列印出來。
執行結果前兩行正確輸出帶緩衝通道的資料,表明緩衝通道在關閉後依然可以存取內部的資料。
執行結果第三行的“0 false”表示通道在關閉狀態下取出的值。0 表示這個通道的預設值,false 表示沒有獲取成功,因為此時通道已經空了。我們發現,在通道關閉後,即便通道沒有資料,在獲取時也不會發生阻塞,但此時取出資料會失敗。