Golang select优先级执行

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()来退出
        }
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值