Golang 用法 select 语句
跟 switch-case 相比,select-case 用法比较单一,它仅能用于 信道/通道 的相关操作
select {
case 表达式1:
<code>
case 表达式2:
<code>
default:
<code>
}
一个简单的例子
func TestSelect01(t *testing.T) {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
c2 <- "hello"
select {
case msg1 := <-c1:
fmt.Println("main goroutine , c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("main goroutine , c2 received: ", msg2)
default:
fmt.Println("main goroutine , No data received.")
}
}
输出
main goroutine , c2 received: hello
在运行 select 时,会遍历所有(如果有机会的话)的 case 表达式,只要有一个信道有接收到数据,那么 select 就结束,所以输出c2。
避免造成死锁
- select 在执行过程中,必须命中其中的某一分支。
- 如果在遍历完所有的 case 后,若没有命中,任何一个 case 表达式,就会进入 default 里的代码分支。
- 但如果你没有写 default 分支,select 就会阻塞,直到有某个 case 可以命中,而如果一直没有命中,select 就会抛出 deadlock 的错误,
就像下面这样子
func TestSelect01(t *testing.T) {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
select {
case msg1 := <-c1:
fmt.Println("main goroutine , c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("main goroutine , c2 received: ", msg2)
}
}
输出如下
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select]:
main.main()
d:/demo/select_demo.go:11 +0xbb
exit status 2
解决这个问题的方法有两种,一个是,养成好习惯,在 select 的时候,也写好 default 分支代码,尽管你 default 下没有写任何代码。
func TestSelect01(t *testing.T) {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
// c2 <- "hello"
select {
case msg1 := <-c1:
fmt.Println("main goroutine , c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("main goroutine , c2 received: ", msg2)
default:
fmt.Println("main goroutine , No data received.")
}
}
输出
main goroutine , No data received.
另一个是,让其中某一个信道可以接收到数据
func TestSelect01(t *testing.T) {
c1 := make(chan string, 1)
c2 := make(chan string, 1)
go func() {
time.Sleep(3 * time.Second)
c2 <- "hello"
}()
select {
case msg1 := <-c1:
fmt.Println("main goroutine , c1 received: ", msg1)
case msg2 := <-c2:
fmt.Println("main goroutine , c2 received: ", msg2)
}
}
输出
main goroutine , c2 received: hello