概述
select
相似 switch
, 蕴含一系列逻辑分支和一个可选的默认分支。每一个分支对应通道上的一次操作 (发送或接管),
能够将 select
了解为专门针对通道操作的 switch
语句。
语法规定
select {case v1 := <- ch1:// do something ... case v2 := <- ch2:// do something ...default:// do something ...}
执行程序
- 当同时存在多个满足条件的通道时,随机抉择一个执行
如果没有满足条件的通道时,检测是否存在 default 分支
- 如果存在则执行
- 否则阻塞期待
通常状况下,把含有 default 分支
的 select
操作称为 无阻塞通道操作
。
例子
随机执行一个
package mainimport ( "fmt" "time")func main() { ch1 := make(chan string) ch2 := make(chan string) done := make(chan bool) go func() { ch1 <- "hello" }() go func() { ch2 <- "world" }() go func() { done <- true }() time.Sleep(time.Second) // 休眠 1 秒 // 此时 3 个通道应该都满足条件,select 会随机抉择一个执行 select { case msg := <-ch1: fmt.Printf("ch1 msg = %s\n", msg) case msg := <-ch2: fmt.Printf("ch2 msg = %s\n", msg) case <-done: fmt.Println("done !") } close(ch1) close(ch2) close(done)}// $ go run main.go// 输入如下,你的输入可能和这里的不一样, 多运行几次看看成果/** ch1 msg = hello*/
default (无阻塞通道操作)
package mainimport ( "fmt" "time")func main() { ch1 := make(chan string) ch2 := make(chan string) done := make(chan bool) go func() { time.Sleep(time.Second) ch1 <- "hello" }() go func() { time.Sleep(time.Second) ch2 <- "world" }() go func() { time.Sleep(time.Second) done <- true }() // 此时 3 个通道都在休眠中, 不满足条件,select 会执行 default 分支 select { case msg := <-ch1: fmt.Printf("ch1 msg = %s\n", msg) case msg := <-ch2: fmt.Printf("ch2 msg = %s\n", msg) case <-done: fmt.Println("done !") default: fmt.Println("default !") } close(ch1) close(ch2) close(done)}// $ go run main.go// 输入如下/** default !*/
和 for 搭配应用
通过在 select
外层加一个 for
循环,能够达到 有限轮询
的成果。
package mainimport ( "fmt" "time")func main() { ch1 := make(chan string) ch2 := make(chan string) done := make(chan bool) go func() { // ch1 goroutine 输入 1 次 fmt.Println("[ch1 goroutine]") time.Sleep(time.Second) ch1 <- "hello" }() go func() { // ch2 goroutine 输入 2 次 for i := 0; i < 2; i++ { fmt.Println("[ch2 goroutine]") time.Sleep(time.Second) } ch2 <- "world" }() go func() { // done goroutine 输入 3 次 for i := 0; i < 3; i++ { fmt.Println("[done goroutine]") time.Sleep(time.Second) } done <- true }() for exit := true; exit; { select { case msg := <-ch1: fmt.Printf("ch1 msg = %s\n", msg) case msg := <-ch2: fmt.Printf("ch2 msg = %s\n", msg) case <-done: fmt.Println("done !") exit = false // 通过变量管制外层 for 循环退出 } } close(ch1) close(ch2) close(done)}// $ go run main.go// 输入如下,你的输入程序可能和这里的不一样/** [done goroutine] [ch2 goroutine] [ch1 goroutine] ch1 msg = hello [done goroutine] [ch2 goroutine] ch2 msg = world [done goroutine] done !*/
从输入后果看,[ch1 goroutine]
输入了 1 次,[ch2 goroutine]
输入了 2 次,[done goroutine]
输入了 3 次。