Go裏面提供了一個關鍵字 select
, 經過 select
能夠監聽channel上的數據流動.git
select
的用法與 switch
語言很是相似, 由 select
開始一個新的選擇塊, 每一個選擇條件由 case
語句來描述.網站
與 switch
語句相比, select
有比較多的限制, 其中最大的一條限制就是每一個case語句裏必須是一個IO操做.ui
大體的結構以下:code
select { case <- chan1: // 若是chan1成功讀到數據, 則進行該case處理語句 case chan2 <- -1: // 若是成功向chan2寫入數據, 則進行該case處理語句 default: // 若是上面都沒有成功, 則進入default處理流程 }
在一個 select
語句中, Go語言會按照順序從頭到尾評估每個發送和接收的語句.事件
若是其中的任意一條語句能夠繼續執行(即沒有阻塞), 那麼就從那些能夠執行的語句中任意選擇一條來使用.ci
若是沒有任意一條語句能夠執行(即全部的通道都被阻塞), 那麼有兩種可能的狀況:get
package main import ( "fmt" "runtime" "time" ) func main() { ch := make(chan int) // 用來進行數據通訊的channel quit := make(chan bool) // 用來判斷是否退出的channel go func() { // 寫數據 for i:=0; i < 5; i++ { ch <- i time.Sleep(time.Second) } close(ch) quit <- true // 通知主go程 退出 runtime.Goexit() }() for { select { case num := <- ch: fmt.Println("讀到: ", num) case <- quit: return //break // break 跳出select循環 } fmt.Println("============") } }
結果:博客
讀到: 0 ============ 讀到: 1 ============ 讀到: 2 ============ 讀到: 3 ============ 讀到: 4 ============ 讀到: 0 ============ 讀到: 0 ============
注意, 由於是任意挑選一個case執行, 因此最後的 讀到:0 的數量至關因而個隨機數.it
因此, 總結下select的注意事項:for循環
package main import ( "fmt" "runtime" ) func fibonacci(ch <-chan int, quit <-chan bool) { for { select { case num := <-ch: fmt.Println(num) case <-quit: //return runtime.Goexit() } } } func main() { ch := make(chan int) quit := make(chan bool) go fibonacci(ch, quit) x, y := 1, 1 for i := 0; i < 50; i++ { ch <- x x, y = y, x+y } quit <- true }
若是不用select的話, 每個case都要建立一個go程去處理, 這樣的話太浪費了, 而用select的話, 只須要一個go程就能夠了.
有時候會出現goroutine阻塞的狀況, 那麼咱們如何避免整個程序進入阻塞的狀況呢?咱們能夠利用select來設置超時, 經過以下的方式來實現:
示例代碼:
package main import ( "fmt" "time" ) func main() { ch := make(chan int) timeOut := make(chan bool) go func() { for { select { case num := <- ch: fmt.Println("num: ", num) case <- time.After(5 * time.Second): fmt.Println("timeout") timeOut <- true return } } }() ch <- 666 <- timeOut // 主go程, 阻塞等待子go程通知, 退出 fmt.Println("finish.") }
select監聽time.After() 中channel的讀事件, 若是定時時間到, 系統會向該channel中寫入系統當前時間.
歡迎訪問個人我的網站:
李培冠博客:lpgit.com