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 實現該需求