Go微服务实战
上QQ阅读APP看书,第一时间看更新

5.2.5 select

Go语言作为天生的多线程语言,经常要在程序中同时使用或处理多个channel,并且经常需要根据不同的channel读写进行不同的操作。所以,Go语言提供了select关键字。可以把select理解为switch case,只是select是根据不同的channel通信进入不同的处理代码。

select有监听channel通信的作用,当有通信发生时就触发相应的代码块。

select的基本用法和大致结构如下:


select{
case <-ch1:
    //如果从ch1读取数据,则执行该部分代码块
case ch2 <- 1 :
    //如果向ch2写入数据,则执行该部分代码块
default:
    //如果前面的case都不满足,则执行此部分代码块
}

下面通过一个简单的例子来看一下select的使用:


book/ch05/5.2/select/main.go
1. package main
2.
3. import (
4.     "fmt"
5. )
6.
7. func main() {
8.      ch1 := make(chan int)
9.      ch2 := make(chan int)
10.     go func() {
11.         //time.Sleep(3*time.Second)
12.         ch1 <- 1
13.     }()
14.     go func() {
15.         //time.Sleep(5*time.Second)
16.         ch2 <- 2
17.     }()
18.
19.     select {
20.     case <-ch1:
21.         fmt.Println("can read from ch1")
22.     case <-ch2:
23.         fmt.Println("can read from ch2")
24.     //default:
25.     //  fmt.Println("default...")
26.     }
27. }

这是一个非常简单的使用select的例子。

第8行和第9行定义了两个int型通道。

第10行至第17行,启动了两个goroutine,运行了两个匿名函数,函数的作用分别是为ch1和ch2写数据。

第19行至第27行,通过select判断是ch1还是ch2可读,进而打印不同的信息。要注意的是,select内的case不是顺序执行的,并非每次都是先判断写在前面的case,而是会并行判断,当两个条件都满足的时候会相对公平地选择一个执行。读者可以执行上述代码,看一下是不是打印ch1相关信息和打印ch2相关信息的概率差不多。

注意,对于注释掉的第24行和第25行,如果去掉注释,很可能就会执行default,因为主goroutine和前面启动的两个goroutine是并发执行,在判断case的时候数据往往还没有写入ch1和ch2,这时候就会执行default。而当去掉default的时候,因为没有满足条件的case,所以select会进入阻塞状态。读者可以去掉第11行和第15行的注释再执行代码,看一下结果是否又会不同。

注意

select可以管理和编排多个channel,是并发编程当中非常重要的应用。但是一定要注意使用selelct时的死锁问题,在开发过程中要格外小心。