面試題
這是Go Quiz系列中關於channel的第2篇,涉及channel
被close後的特性,以及在select
和channel
一起使用時的注意事項。
這道題目來源於Google的工程師Valentin Deleplace。
package main
import "fmt"
func main() {
data := make(chan int)
shutdown := make(chan int)
close(shutdown)
close(data)
select {
case <-shutdown:
fmt.Print("CLOSED, ")
case data <- 1:
fmt.Print("HAS WRITTEN, ")
default:
fmt.Print("DEFAULT, ")
}
}
- A: 進入default分支,列印"DEFAULT, "
- B: 進入shutdown分支,列印"CLOSED, "
- C: 進入data分支,列印"HAS WRITTEN, "
- D: 程式會panic
- E: 程式可能panic,也可能列印"CLOSED, "
這道題主要考察以下知識點:
channel
被關閉後,從channel
接收資料和往channel
傳送資料會有什麼結果?select
的執行機制是怎樣的?
解析
- 對於無緩衝區的
channel
,往channel
傳送資料和從channel
接收資料都會阻塞。 對於
nil channel
和有緩衝區的channel
,收發資料的機制如下表所示:channel nil 空的 非空非滿 滿了 往channel傳送資料 阻塞 傳送成功 傳送成功 阻塞 從channel接收資料 阻塞 阻塞 接收成功 接收成功 關閉channel panic 關閉成功 關閉成功 關閉成功 channel
被關閉後:- 往被關閉的
channel
傳送資料會觸發panic。 從被關閉的
channel
接收資料,會先讀完channel
裡的資料。如果資料讀完了,繼續從channel
讀資料會拿到channel
裡儲存的元素型別的零值。data, ok := <- c
對於上面的程式碼,如果channel
c
關閉了,繼續從c
裡讀資料,當c
裡還有資料時,data
就是對應讀到的值,ok
的值是true
。如果c
的資料已經讀完了,那data
就是零值,ok
的值是false
。channel
被關閉後,如果再次關閉,會引發panic。
- 往被關閉的
select
的執行機制如下:- 選取一個可執行不阻塞的
case
分支,如果多個case
分支都不阻塞,會隨機選一個case
分支執行,和case
分支在程式碼裡寫的順序沒關係。 - 如果所有
case
分支都阻塞,會進入default
分支執行。 - 如果沒有
default
分支,那select
會阻塞,直到有一個case
分支不阻塞。
- 選取一個可執行不阻塞的
根據以上規則,本文最開始的題目,在執行的時候
- data和shutdown這2個channel都被關閉了。
- 對於關閉的channel,從channel裡接收資料,拿到的是channel的儲存的元素型別的零值,因此
case <-shutdown
這個case分支不會阻塞。 - 對於關閉的channel,向其傳送資料會引發panic,因此
case data <- 1
這個case分支不會阻塞,會引發panic。 - 因此這個select語句執行的時候,2個case分支都不會阻塞,都可能執行到。如果執行的是
case <-shutdown
這個case分支,會列印"CLOSED, "。如果執行的是case data <- 1
這個case分支,會導致程式panic。
因此本題的答案是E
。
加餐
可以回顧Go quiz系列中關於channel的第一道題目,加深對channel的理解。
題目連結地址:channel面試題和注意事項
開源地址
文章和示例程式碼開源地址在GitHub: https://github.com/jincheng9/...
公眾號:coding進階
個人網站:https://jincheng9.github.io/
知乎:https://www.zhihu.com/people/...