select
概念说明
select 用于同时等待多个 channel 操作。
哪个 case 的 channel 先准备好,就执行哪个分支。
如果多个分支同时可执行,Go 会随机选择一个。
因此不要把业务逻辑建立在固定选择顺序上。
语法/规则
select 的每个 case 通常对应一次 channel 发送或接收。- 如果没有任何 channel 准备好,
select 会阻塞。 - 可以使用
default 分支避免阻塞。 - 可以配合
time.After 实现超时控制。 - 多个分支同时就绪时,执行哪个分支是不固定的。
select 同时等待多个消息示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| package main
import (
"fmt"
"time"
)
func main() {
fast := make(chan string)
slow := make(chan string)
go func() {
time.Sleep(50 * time.Millisecond)
fast <- "fast channel"
}()
go func() {
time.Sleep(100 * time.Millisecond)
slow <- "slow channel"
}()
for i := 0; i < 2; i++ { // 一共接收两次消息
select {
case msg := <-fast:
fmt.Println("receive:", msg) // fast 先准备好时,先进入这个分支
case msg := <-slow:
fmt.Println("receive:", msg) // slow 准备好时,进入这个分支
}
}
}
|
输出结果:
1
2
| receive: fast channel
receive: slow channel
|
这个示例体现了什么
select 会同时等待 fast 和 slow 两个 channel。- 哪个 channel 先准备好,就先执行哪个
case。 - 第一次进入
select 时,fast 更早发送数据,所以先输出 fast channel。 - 第二次进入
select 时,再接收 slow 里的数据。 - 如果多个
case 同时准备好,select 选择哪个分支是不固定的。
这段代码是怎么运行的
先看发送方:
1
2
3
4
5
6
7
8
9
| go func() {
time.Sleep(50 * time.Millisecond)
fast <- "fast channel"
}()
go func() {
time.Sleep(100 * time.Millisecond)
slow <- "slow channel"
}()
|
可以先理解成:
1
2
| 50ms 后,向 fast 发送一条消息
100ms 后,向 slow 发送一条消息
|
再看接收方:
1
2
3
4
5
6
7
8
| for i := 0; i < 2; i++ {
select {
case msg := <-fast:
fmt.Println("receive:", msg)
case msg := <-slow:
fmt.Println("receive:", msg)
}
}
|
它不是“for 一直空转检查”。
真正等待消息的是 select。
运行过程可以理解成:
1
2
3
4
5
6
7
8
9
10
11
12
13
| 第 1 次循环
-> 进入 select
-> fast 和 slow 都还没消息
-> select 阻塞等待
-> 50ms 后 fast 收到消息
-> 执行 case msg := <-fast
第 2 次循环
-> 再次进入 select
-> slow 还没消息
-> select 继续阻塞等待
-> 100ms 后 slow 收到消息
-> 执行 case msg := <-slow
|
所以这里:
表示“总共接收 2 次消息”。
如果你不知道要接收多少次,而是想一直等,就会写成:
1
2
3
4
5
6
7
8
| for {
select {
case msg := <-fast:
fmt.Println("receive:", msg)
case msg := <-slow:
fmt.Println("receive:", msg)
}
}
|
常见错误
- 以为
select 会按 case 书写顺序检查,实际多个分支同时就绪时会随机选择。 - 没有
default 且所有 channel 都不就绪,导致程序阻塞。 - 在
default 分支里写空循环,可能造成 CPU 空转。 - 把
select 和普通 switch 混淆,select 专门用于 channel 操作。