Go Quiz: 從Go面試題看channel在select場景下的注意事項

coding進階發表於2022-01-30

面試題

這是Go Quiz系列中關於channel的第2篇,涉及channel被close後的特性,以及在selectchannel一起使用時的注意事項。

這道題目來源於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的執行機制是怎樣的?

解析

  1. 對於無緩衝區的channel,往channel傳送資料和從channel接收資料都會阻塞。
  2. 對於nil channel和有緩衝區的channel,收發資料的機制如下表所示:

    channelnil空的非空非滿滿了
    往channel傳送資料阻塞傳送成功傳送成功阻塞
    從channel接收資料阻塞阻塞接收成功接收成功
    關閉channelpanic關閉成功關閉成功關閉成功
  3. channel被關閉後:

    • 往被關閉的channel傳送資料會觸發panic。
    • 從被關閉的channel接收資料,會先讀完channel裡的資料。如果資料讀完了,繼續從channel讀資料會拿到channel裡儲存的元素型別的零值。

      data, ok := <- c 

      對於上面的程式碼,如果channel c關閉了,繼續從c裡讀資料,當c裡還有資料時,data就是對應讀到的值,ok的值是true。如果c的資料已經讀完了,那data就是零值,ok的值是false

    • channel被關閉後,如果再次關閉,會引發panic。
  4. 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

相關文章