select功能
- 每次执行select,都会只执行其中1个case或者执行default语句。
- 当没有case或者default可以执行时,select则阻塞,等待直到有1个case可以执行。
- 当有多个case可以执行时,则随机选择1个case执行。
- case后面跟的必须是读或者写通道的操作,否则编译出错。
select示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import "log"
func main() {
readCh := make(chan int, 1)
writeCh := make(chan int, 1)
y := 1
select {
case x := <-readCh:
log.Printf("Read %d \n", x)
case writeCh <- y:
log.Printf("Write %d \n", y)
default:
log.Println("Do what you want.")
}
}
|
运行结果:
luozhibo@bogon 04selectDemo % go run 01demo.go
2020/03/07 15:01:39 Write 1
说明:
- default 不是必须的
- 顺序执行,当执行到第二个case时,执行,有case执行,所以default不会执行
select进阶使用
三个主要特性如下:
- nil的通道永远阻塞
- 如何跳出for-select
- select{}
1、nil的通道永远阻塞
当case上读一个通道时,如果这个通道时nil,则该case永远阻塞。
功能妙用:
当某个通道关闭了,不用了,则把该通道设置为nil即可。
例如:以下示例程序实现了等待两个输入通道都关闭后才退出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// combine 两个通道都关闭后才退出
func combine(inCh1, inCh2 <-chan int) <-chan int {
// 输出通道
out := make(chan int)
// 启动协程合并数据
go func() {
defer close(out)
for {
select {
case x, open := <-inCh1:
if !open {
inCh1 = nil
continue
}
out <- x
case x, open := <-inCh2:
if !open {
inCh2 = nil
continue
}
out <- x
} // end select
// 当ch1和ch2都关闭是才退出
if inCh1 == nil && inCh2 == nil {
break
}
} // end for
}()
return out
}
|
2、如何跳出 for-select
break 在select内失效,不能跳出for-select循环。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
func consume(inCh <-chan int) {
i := 0
for {
fmt.Printf("for: %d\n", i)
select {
case x, open := <-inCh:
if !open {
break // 使用break不能退出for-select循环
}
fmt.Printf("read: %d\n", x)
}
i++
}
fmt.Println("combine-routine exit")
}
|
退出方法:
- 在满足条件的case内,使用return,如果有结尾工作,尝试交给defer。
- 在select外for内使用break挑出循环,如combine函数。
- 使用goto。
3、select
select{}的效果等价于创建了1个通道,直接从通道读数据:
ch := make(chan int)
<-ch
select的应用场景
-
多通道处理
-
无决阻塞的读、写通道。
即使通道是带缓存的,也是存在阻塞的情况,使用select可以完美的解决阻塞读写。
-
设置超时时间,一旦在超时时间内无法完成,则停止处理。
原文地址:http://www.manoner.com/post/GoLand/Go%E8%AF%AD%E8%A8%80select%E4%BD%BF%E7%94%A8/
|