面試題
這是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/...
References
- https://twitter.com/val_delep...
- https://github.com/jincheng9/...
- https://github.com/jincheng9/...
- https://github.com/jincheng9/...