golang 面試官:for select 時,如果通道已經關閉會怎麼樣?如果只有一個 case 呢?

今天咱們聊點技術性的東西,具體說一下 Go 語言中的 for select 語句,特別是在面試中經常被問到的兩個問題。

題目 1:for select 語句中,如果通道已經關閉了,會怎麼樣?

這道題乍一聽簡單,但其實是面試官最喜歡用來考察你是否真正理解 Go 中通道機制的題目之一。假設你在寫一個需要從通道讀取數據的程序,然後突然有人關了通道,你會怎麼處理?程序會怎麼樣?如果你把這個問題想得很簡單,你可能會以爲它只是個 “錯誤處理” 的問題,但是 Go 的通道在關閉時的行爲是有特殊規則的,搞清楚這些規則對寫出高質量代碼非常重要。

閉合通道的行爲

在 Go 中,通道是一個用於在 goroutine 之間傳遞數據的管道。如果你對 select 語句不太熟悉,我可以簡短地回顧一下它的工作方式。select 就是 Go 語言中多路複用的選擇語句,它監聽多個通道的消息,並在某個通道有數據時執行對應的代碼塊。代碼大概像這樣:

select {
case msg := <-ch1:
    fmt.Println("Received:", msg)
case msg := <-ch2:
    fmt.Println("Received:", msg)
case <-time.After(1 * time.Second):
    fmt.Println("Timeout")
}

在這個例子裏,select 等待從 ch1ch2 獲取數據,或者等到超時。如果這些通道都沒有數據,它就會阻塞等待。

那麼,如果某個通道已經關閉了,會發生什麼呢?這時候通道的行爲就很特別了。我們來逐步分析:

  1. 從已關閉的通道讀取數據: 如果你從已經關閉的通道讀取數據,Go 會立刻返回該通道的零值。舉個例子,如果你有一個 int 類型的通道,當它關閉時,從中讀取數據將始終返回 0(零值)。你可能覺得這有點兒反直覺,但這就是 Go 的設計。

  2. select 中的關閉通道: 如果你在 select 語句中選擇了一個已經關閉的通道,它將立刻進入對應的 case 分支。如果有多個通道同時滿足條件,它會選擇已經關閉的通道並處理它。

  3. 是否會阻塞? 如果通道關閉時,且沒有更多的數據要發送,select 將不再阻塞並立即跳到對應的 case。但是,如果你在 select 中一直在等待某個通道的消息,它會在通道關閉後正常執行,但不會再等待該通道的數據了。

代碼示例

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int, 1)
    close(ch) // 關閉通道

    select {
    case msg := <-ch:
        fmt.Println("Received:", msg)
    default:
        fmt.Println("No data received")
    }
}

在這段代碼裏,通道 ch 在被關閉後會直接進入 select 語句的 case msg := <-ch: 分支,輸出的是 0,因爲 ch 被關閉並且已經沒有數據了。如果你把 ch 關閉後再發送數據,數據不會被接收。實際上,關閉通道時需要小心,尤其是在高併發環境下,容易導致數據丟失。

題目 2:如果 select 語句中只有一個 case,會怎麼樣?

這個問題相對簡單,但是也能檢驗你對 select 的理解。

假設你在使用 select 時,只有一個 case,會發生什麼呢?根據 Go 語言的文檔,select 是一個阻塞操作,如果只有一個 case 並且通道有數據可讀,select 會立即執行該 case。如果沒有數據可讀,它就會一直阻塞等待數據。

那麼,這個問題的本質是,select 會一直阻塞直到某個通道能夠處理數據。如果只有一個 case,那麼只有該通道準備好時,select 纔會被觸發。

示例代碼

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)

    // 這裏啓動一個 goroutine 來給通道發送數據
    go func() {
        ch <- 42
    }()

    select {
    case msg := <-ch:
        fmt.Println("Received:", msg)
    }
}

在這段代碼中,我們只有一個 case,並且通過一個 goroutine 向通道 ch 發送數據。當數據準備好時,select 會立刻執行。如果沒有數據,它就會一直阻塞在這裏,不會繼續執行下面的代碼。

總結

這兩個問題看似簡單,但它們分別考察了你對 Go 通道工作機制的掌握程度,尤其是通道關閉時的行爲。正確理解 Go 的通道關閉與 select 的阻塞特性,是避免寫出錯誤代碼的關鍵。如果你正在爲面試做準備,記得在面試時解釋清楚這些行爲。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Fjf9x95tJXlFxsHt_415wg