1. 程式人生 > 其它 >golang的select典型用法

golang的select典型用法

golang 的 select 的功能和select, poll, epoll相似, 就是監聽 IO 操作,當 IO 操作發生時,觸發相應的動作。

示例:

ch1 := make (chan int, 1)
ch2 := make (chan int, 1)

...

select {
case <-ch1:
    fmt.Println("ch1 pop one element")
case <-ch2:
    fmt.Println("ch2 pop one element")
}

注意到 select 的程式碼形式和 switch 非常相似, 不過 select 的 case 裡的操作語句只能是【IO 操作】 。

此示例裡面 select 會一直等待等到某個 case 語句完成, 也就是等到成功從 ch1 或者 ch2 中讀到資料。 則 select 語句結束。

【使用 select 實現 timeout 機制】

如下:

timeout := make (chan bool, 1)
go func() {
    time.Sleep(1e9) // sleep one second
    timeout <- true
}()
ch := make (chan int)
select {
case <- ch:
case <- timeout:
    fmt.Println("timeout!")
}

當超時時間到的時候,case2 會操作成功。 所以 select 語句則會退出。 而不是一直阻塞在 ch 的讀取操作上。 從而實現了對 ch 讀取操作的超時設定。

下面這個更有意思一點。

當 select 語句帶有 default 的時候:

ch1 := make (chan int, 1)
ch2 := make (chan int, 1)

select {
case <-ch1:
    fmt.Println("ch1 pop one element")
case <-ch2:
    fmt.Println("ch2 pop one element")
default:
    fmt.Println("default")
}

此時因為 ch1 和 ch2 都為空,所以 case1 和 case2 都不會讀取成功。 則 select 執行 default 語句。

就是因為這個 default 特性, 我們可以使用 select 語句來檢測 chan 是否已經滿了。

如下:

ch := make (chan int, 1)
ch <- 1
select {
case ch <- 2:
default:
    fmt.Println("channel is full !")
}

因為 ch 插入 1 的時候已經滿了, 當 ch 要插入 2 的時候,發現 ch 已經滿了(case1 阻塞住), 則 select 執行 default 語句。 這樣就可以實現對 channel 是否已滿的檢測, 而不是一直等待。

比如我們有一個服務, 當請求進來的時候我們會生成一個 job 扔進 channel, 由其他協程從 channel 中獲取 job 去執行。 但是我們希望當 channel 瞞了的時候, 將該 job 拋棄並回復 【服務繁忙,請稍微再試。】 就可以用 select 實現該需求。

關於垃圾回收

c++寫久了的人, 剛接觸 golang 的時候最不能理解的就是為什麼作者要支援垃圾回收。 不管是從垃圾回收器的實現上看, 還是對於程式設計師程式設計習慣的養成方面, 都避免不了編寫出的程式效能損失。 但是寫了幾天 golang 之後, 又覺得有垃圾回收確實大大減輕程式設計師的心智負擔, 降低程式設計門檻,提高程式設計效率。 讓我聯想到 彙編 和 C語言 的關係, 即使 C語言的效能不如彙編寫出來的高, 但是後者還是顛覆了前者。

參考

順便說說,我自己對這兩本書的評價:

【go語言程式設計】和【go語言程式設計】這兩本書我都看過。 前者偏向於學院派教科書,語言表述非常囉嗦。 後者偏向於工程派,我在看完書之後寫程式碼的時候, 有疑問了會經常翻看【go 語言程式設計】查詢對應的內容看, 作者表述言簡意賅,一翻我就能找到我想要的答案, 讓人感覺是自己想問的,作者早就思考過並已經很好的介紹和解釋。 說白了,如果你是程式設計初學者,那麼請看【go語言程式設計】。 如果是有良好基礎的開發者,直接看【go語言程式設計】會高效很多。