go选择器select
select
select(选择器),可以同时等待多个通道操作。
将协程、通道和选择器结合,是Go的一个强大特性。
在Go语言中,select语句用于处理一个或多个channel操作,类似switch语句,但用于通信操作而不是切换操作。
select语句用于等待多个通信操作中的一个完成,然后执行相应的动作。
select语句使得goroutine可以等待多个channel操作,使得程序更具有并发性。
- select语句的基础语法
select {
case <- channel1:
//当channel1可以读取时执行
case data := <= channel2:
//当channel2可以读取时执行此处代码,同时将读取的数据复制给data变量
case channel3 <- value:
//当channel3可以写入时执行此处的代码,将value写入channel3
default:
//当上述case语句都不满足时,执行此处的代码
}
select语句中的每个case语句必须是一个channel的发送操作、接收操作或者是空操作(空操作表示什么都不做),select会阻塞,直到某个case语句可以执行,然后它会执行那个case,其他的被忽略,如果有多个case同时满足条件,会随机选择一个执行。
例子1
package main
import(
"fmt"
"time"
)
func main() {
//定义channel
channel1 := make(chan string)
channel2 := make(chan string)
channel3 := make(chan string)
channel4 := make(chan string)
channel5 := make(chan string)
channel6 := make(chan string)
channel7 := make(chan string)
//起一个go向channel写入数据
go func() {
time.Sleep(2 * time.Second)
channel1 <- "Hello from channel 1"
}()
go func() {
time.Sleep(2 * time.Second)
channel2 <- "Hello from channel 2"
}()
go func() {
time.Sleep(2 * time.Second)
channel3 <- "Hello from channel 3"
}()
go func() {
time.Sleep(2 * time.Second)
channel4 <- "Hello from channel 4"
}()
go func() {
time.Sleep(2 * time.Second)
channel5 <- "Hello from channel 5"
}()
go func() {
time.Sleep(2 * time.Second)
channel6 <- "Hello from channel 6"
}()
go func() {
time.Sleep(2 * time.Second)
channel7 <- "Hello from channel 7"
}()
select {
case msg1 := <-channel1:
fmt.Println(msg1)
case msg2 := <-channel2:
fmt.Println(msg2)
case msg3 := <-channel3:
fmt.Println(msg3)
case msg4 := <-channel4:
fmt.Println(msg4)
case msg5 := <-channel5:
fmt.Println(msg5)
case msg6 := <-channel6:
fmt.Println(msg6)
case msg7 := <-channel7:
fmt.Println(msg7)
}
}
上述例子中,起了7个goroutine并发向channel写入数据。
使用select语句,程序会等待最早准备好的channel,并将其消息打印出来。这种方式可以确保程序不会因为等待一个channel的时间过长而被阻塞,能够充分利用并发性。
例子2
for + select 组合
参考:https://go.dev/tour/concurrency/5
- 执行流程步骤
- 这是常见的并发模式,表示"不断监听多个channel事件"。
- 每一次循环,select会随机选择一个准备好的通道分支执行。
- 如果没有一个通道准备好,select会阻塞,直到有通道就绪。
- select 的每轮执行都是新的选择,它不会轮训,而是随机选择准备好的case。
- select + channel 是实现非阻塞控制流的关键
使用方式
for {
select {
// 多个通信分支
}
}
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
- 输出结果
0
1
1
2
3
5
8
13
21
34
quit
浙公网安备 33010602011771号