概述
select
相似 switch
, 蕴含一系列逻辑分支和一个可选的默认分支。每一个分支对应通道上的一次操作 (发送或接管),
能够将 select
了解为专门针对通道操作的 switch
语句。
语法规定
select {
case v1 := <- ch1:
// do something ...
case v2 := <- ch2:
// do something ...
default:
// do something ...
}
执行程序
- 当同时存在多个满足条件的通道时,随机抉择一个执行
-
如果没有满足条件的通道时,检测是否存在 default 分支
- 如果存在则执行
- 否则阻塞期待
通常状况下,把含有 default 分支
的 select
操作称为 无阻塞通道操作
。
例子
随机执行一个
package main
import (
"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 main
import (
"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 main
import (
"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 次。