select关键词
go语言中select关键词用于监听case语句对应的chan,并执行其下相应的代码,直到case下的代码执行结束才会执行另一个发生的chan下面的代码。有点类似于switch。
select {
case x := <-ch1:
fmt.Println(x)
case ch2 <- 1:
fmt.Println("push 1")
}
空select
select{
}
空的 select 语句会直接阻塞当前的goroutine,使得该goroutine进入无法被唤醒的休眠状态。
只有一个case
select {
case <-ch1:
fmt.Println("recv")
}
上面的代码,当ch1可读时会执行打印操作,否则就会阻塞。
有default语句
select {
case <-ch1:
fmt.Println("recv")
default:
time.Sleep(time.Second)
}
上面的代码,当ch1可读时会执行打印操作,否则就执行default语句中的代码,这里就相当于做了一个非阻塞的channel读取操作
总结
- select 不存在任何的 case:永久阻塞当前 goroutine
- select 只存在一个 case:阻塞的发送/接收
- select 存在多个 case:随机选择一个满足条件的case执行
- select 存在 default,其他case都不满足时:执行default语句中的代码
实现select中的优先级
参考k8s源码中优先级select的处理
func (tc *NoExecuteTaintManager) worker(worker int, done func(), stopCh <-chan struct{}) {
defer done()
// When processing events we want to prioritize Node updates over Pod updates,
// as NodeUpdates that interest NoExecuteTaintManager should be handled as soon as possible -
// we don't want user (or system) to wait until PodUpdate queue is drained before it can
// start evicting Pods from tainted Nodes.
for {
select {
case <-stopCh:
return
case nodeUpdate := <-tc.nodeUpdateChannels[worker]:
tc.handleNodeUpdate(nodeUpdate)
tc.nodeUpdateQueue.Done(nodeUpdate)
case podUpdate := <-tc.podUpdateChannels[worker]:
// If we found a Pod update we need to empty Node queue first.
priority:
for {
select {
case nodeUpdate := <-tc.nodeUpdateChannels[worker]:
tc.handleNodeUpdate(nodeUpdate)
tc.nodeUpdateQueue.Done(nodeUpdate)
default:
break priority
}
}
// After Node queue is emptied we process podUpdate.
tc.handlePodUpdate(podUpdate)
tc.podUpdateQueue.Done(podUpdate)
}
}
}
我目前需要这个优先级select的应用场景一般是,当我的程序catch到了异常信号需要退出时,我会在退出前把select下其他chan都执行完了再退出
//异常退出的信号
var kExitSignals = []os.Signal{
syscall.SIGTERM,
syscall.SIGINT,
syscall.SIGSEGV,
syscall.SIGBUS,
syscall.SIGQUIT,
}
int main() {
exitChan := make(chan os.Signal, 1)
signal.Notify(exitChan, kExitSignals...)
handleCh := make(chan struct{}, 10)
//goroutine with input handleCh
for {
select {
case <-handleCh:
//do some handle
case signl := <-exitChan:
//收到了退出信号,也要保证先把chan中的数据处理了再退出
priority:
for {
select {
case <-handleCh:
// do some handle
default:
break priority
}
}
log.Info("catch signal:", signl)
//做一些清理工作和资源回收
//os.Exit(1)
//如果是服务器的可以使用server.Stop()或者server.GraceStop()来退出
}
}
}