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