select 语句可以处理 channel 的 send 和 recv,send 和 recv 都可以作为 case 分支。如果我们需要处理的 case 分支 数量少,那么使用 select 语句是完全可以的,例如:
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
如果需要处理很多个 case 分支,例如 100 个、1000 个,那么就可以使用反射来管理多个 case 分支。
func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)
你可以将一组 SelectCase 传入,SelectCase 的定义如下:
const (
_ SelectDir = iota
SelectSend // case Chan <- Send
SelectRecv // case <-Chan:
SelectDefault // default
)
type SelectCase struct {
// 表示 case 的方向,即操作的类型
// SelectSend:表示这是一个发送操作(向 channel 发送数据)
// SelectRecv:表示这是一个接收操作(从 channel 接收数据)
// SelectDefault:表示这是一个默认操作(类似于 select 中的 default 分支)。
Dir SelectDir
Chan Value // 要操作的 channel
Send Value // 要发送的值
}
再来解释一下返回值:
chosen表示被选中的 case 的索引(即cases切片中的下标);recv是从 channel 接收到的值(如果选中的 case 是一个接收操作),否则recv是零值;recvOK表示是否从 channel 接收到值,而不是由于 channel 关闭接收到的零值(也就是说,当recvOK为 true 时则代表 channel 未被关闭)。
最后我们以一个 reflect.Select 的使用示例收尾:
package main
import (
"fmt"
"reflect"
)
func main() {
numChannels := 10
channels := make([]chan int, numChannels)
for i := 0; i < numChannels; i++ {
channels[i] = make(chan int, 1)
}
// 动态构建 SelectCase 切片
var cases []reflect.SelectCase
for i, ch := range channels {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv, // 接收操作
Chan: reflect.ValueOf(ch),
}, reflect.SelectCase{
Dir: reflect.SelectSend, // 发送操作
Chan: reflect.ValueOf(ch),
Send: reflect.ValueOf(i),
})
}
for i := 0; i < numChannels; i++ {
// 使用 reflect.Select 处理多个 channel
chosen, recv, recvOK := reflect.Select(cases)
// 输出结果
if recv.IsValid() {
fmt.Printf("Received value: %v, recvOK: %v\n", recv.Int(), recvOK)
} else {
fmt.Println("send:", chosen)
}
}
}