Select简明教程
select关键字到底有什么作用?下面先来一个简单例子说明:
func main(){
fmt.Println(time.Now().Format("15:04:05") + " Go Select Statement Tutorial")
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "video data"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "audio data"
}()
fmt.Println(time.Now().Format("15:04:05"), "Started goroutine!!")
for i := 0; i < 2; i++ {
select {
case msg1 := <- ch1:
fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg1)
case msg2 := <- ch2:
fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg2)
}
}
}
创建两个Channel和两个子协程分别向一个Channel发送信息,执行看看打印日志:
~ » go run main.go
12:33:29 Go Select Statement Tutorial
12:33:29 Started goroutine!!
12:33:30 Received: video data
12:33:31 Received: audio data
看日志可以发现主协程在启动完两个子协程,到select关键字的时候发生阻塞了!等待第一个子协程睡眠1s后发送消息到Channel,主协程才继续执行打印日志,再过1s第二个子协程发送消息,主协程继续打印。
那么我们可以得出简单的结论,select关键字有点像阻塞版的switch,不过它只针对Channel,等待Channel有信息返回时可以为不同的Channel触发相应的行为。
我们看下官方有关Select关键字的描述:
A "select" statement chooses which of a set of possible send or receive operations will proceed. It looks similar to a "switch" statement but with the cases all referring to communication operations.
一个select语句用来选择哪个case中的发送或接收操作可以被立即执行。它类似于switch语句,但是它的case涉及到channel有关的I/O操作。
或者换一种说法,select就是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。
修改上面的基础使用方法我们会看到什么效果呢?
修改一
修改for循环的次数:
-
修改为1时,for循环次数比子协程次数少,那么在主协程结束前只有一个子协程执行完成,主协程不会等待剩余的子协程。即,一个select关键字只会等待一个Channl。
~ » go run main.go 12:49:51 Go Select Statement Tutorial 12:49:51 Started goroutine!! 12:49:52 Received: video data
-
修改为3时,for循环次数比子协程次数多,那么等待完两个Channel的消息后,会引入死锁状态。
~ » go run main.go 12:52:08 Go Select Statement Tutorial 12:52:08 Started goroutine!! 12:52:09 Received: video data 12:52:09 Finished ch1 12:52:10 Finished ch2 12:52:10 Received: audio data fatal error: all goroutines are asleep - deadlock! goroutine 1 [select]: main.main() ~/main.go:28 +0x32c exit status 2
修改二
在select所包含的代码块里添加default关键字,看看什么效果:
for i := 0; i < 2; i++ {
select {
case msg1 := <- ch1:
fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg1)
case msg2 := <- ch2:
fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg2)
default:
fmt.Println(time.Now().Format("15:04:05"), "execute default")
}
}
打印日志如下:
~ » go run main.go
12:55:05 Go Select Statement Tutorial
12:55:05 Started goroutine!!
12:55:05 execute default
12:55:05 execute default
发现select关键字再也不阻塞等待Channel返回的,继续执行知道主协程结束。
修改三
在case里加入break关键字,会马上返回,不再继续执行。
for i := 0; i < 2; i++ {
select {
case msg1 := <- ch1:
fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg1)
break
fmt.Println(time.Now().Format("15:04:05"), "print2")
case msg2 := <- ch2:
fmt.Println(time.Now().Format("15:04:05"), "Received: ", msg2)
fmt.Println(time.Now().Format("15:04:05"), "print2")
}
}
执行日志如下:
~ » go run main.go
13:01:45 Go Select Statement Tutorial
13:01:45 Started goroutine!!
13:01:46 Finished ch1
13:01:46 Received: video data
13:01:47 Finished ch2
13:01:47 Received: audio data
13:01:47 print3
可以看到case msg1 := <- ch1:
情况发生时,执行完第一句打印,然后遇上break关键字马上停止了下面语句的执行。
午安~