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