go语言select用法
1 原理
操作系统中我们会使用select或者epool等实现 I/O 多路复用, Go 语言中关键字select用法与之类似,但go中的select只能等待Channel中的事件。Go 语言中的 select 够让 Goroutine 同时等待多个 Channel 可读或者可写,在多个文件或者 Channel状态改变之前,select 会一直阻塞当前线程或者 Goroutine。
select 是与 switch 相似的控制结构,与 switch 不同的是,select 中虽然也有多个 case,但是这些 case 中的表达式必须都是 Channel 的收发操作。示例如下:
select {
case testErr := <-dialErr:
require.NoError(t, testErr)
case <-time.After(3 * time.Second):
require.Fail(t, "DialTcp never returned")
}
上述两个case中无论哪一个表达式返回都会立刻执行 case 中的代码,当 select 中的两个 case 同时被触发时,会随机执行其中的一个。
2 场景
Select设计初衷是阻塞当前Goroutine等待channel事件,但是某些场合需要非阻塞方式去执行,比如查询状态。后来就增加了default,default的出现,select可以有各种各样的用法,下面就一一列举select的使用场景:
1. Select中没有case的情况
由于没有case,也就没人唤醒,空的 select 语句会直接阻塞当前 Goroutine,导致 Goroutine 进入无法被唤醒的永久休眠状态。一般情况不应该写出这样的代码。
2. 单一case
如果当前的 select 条件只包含一个 case,那么编译器会将 select 改写成 if 条件语句。会直接阻塞当前 Goroutine,直到该case上的channel事件触发。
3. 多case(无default分支)
常见用法,select在多个case分支上等待,哪个case返回立即执行该case代码,若多个case同时触发,随机执行其中一个,防止单个case出现饿死情况。
4. 多case(有default分支)-非阻塞
常见用法,首先检查所有case 是否满足要求,如果 default 外的case语句都没有事件触发,那么执行 default 里的语句;从而实现非阻塞操作
3 用例和坑
常见用法:
1. 优雅退出
for和select结合使用,Done是外部传入的协程结束标志,default中处理正常的业务逻辑,如果Done事件没有触发,则会一直处理default中的process,并且不会被Done阻塞,如果Done事件触发,此时若正在进行process处理,不会立即退出协程,会等process处理完成后再次进入循环时触发获取Done事件退出。
for {
select {
case <-Done:
return
default:
process()
}
}
2. 为请求设置超长时间
如果为一个阻塞的事件增加超时时间,最好的实现办法就是select加一个time.After的channel,以下以TCP连接为例,显示如何为一个阻塞事件增加超时功能。
go func() {
dialErr <- tcp.DialTcp("", testPort, lc)
}()
select {
case testErr := <-dialErr:
require.NoError(t, testErr)
case <-time.After(3 * time.Second):
require.Fail(t, "DialTcp never returned")
}
3. select常见的坑
select和for循环结合使用时,需要注意,如果select中有break语句,无法跳出for循环,需要结合goto标签实现。
本文来自博客园,作者:易先讯,转载请注明原文链接:https://www.cnblogs.com/gongxianjin/p/17535168.html

浙公网安备 33010602011771号