【Go语言分析selectcase】
- 软件开发
- 2025-07-21 19:17:04

select 基础用法使用场景超时管理无阻塞获取值类事件驱动循环带优先级的任务队列
select是Go语言在语言层面上提供的一个多路复用机制 它可以检测多个channel是否就绪
基础用法Go语言select有如下的几个特点
select中各个case执行顺序是随机的如果某个case中的channel已经ready 则执行相应的语句并退出select流程如果所有的case的channel都没有ready 则有default会走default然后退出select 没有default select将阻塞直至channel readycase后面不一定是读channel 也可以写channel 只要是对channel的操作就可以空的select语句将被阻塞 直至panic 但是通常情况下 我们不会使用空的select语句 因为这会导致一个协程一直阻塞下面我们使用几段代码来一一讲解select的特点
select中各个case执行顺序是随机的 select { case <-chan1: // 如果chan1成功读取到数据 则执行该操作 // ... case chan2 <- 1: // 如果chan2成被写入数据 则执行该操作 // ... }如果说此时 chan1成功读取到了数据的同时chan2成功被写入了数据 那么就会随机选择一个case执行
select没有case 永久阻塞 select { }
如果说我们使用一个协程执行了上面这段代码 那么在该协程会永久阻塞
注意 虽然说Go语言有死锁机制 会自动检测是否所有协程是否被阻塞 但是这是建立在所有协程都进入死锁的情况 如果说只是这一个协程阻塞 其他协程没有被阻塞那么此时不会发生panic错误的
如果所有的case的channel都没有ready 则有default会走default然后退出select 没有default select将阻塞直至channel ready select { case <-chan1: // 如果chan1已经准备好接收数据,则执行此case。 // ... case chan2 <- 1: // 如果chan2已经准备好发送数据,则执行此case。 // ... default: // 如果上述channel都未准备好,立即执行default case。 // ... }
如果说此时没有channel准备好 则立刻执行default语句
使用场景 超时管理 // 设置一个1秒的超时时间 timeout := time.After(1 * time.Second) // 假设这是一个异步操作的结果通道 operationCh := make(chan string) select { case result := <-operationCh: // 处理异步操作的结果 fmt.Println("操作成功:", result) case <-timeout: // 如果1秒钟内操作没有完成,会执行这个case fmt.Println("操作超时") }如果说我们不想无限制的执行该select操作 那么我们可以设置一个类似超时器 设定一个超时时间 如果说在该时间内没有读取到数据 那么我们就终止该select
无阻塞获取值 // 创建一个通道 messageChan := make(chan string, 1) // 缓冲通道,避免死锁 // 非阻塞地从通道接收数据 select { case msg := <-messageChan: fmt.Println("收到消息:", msg) default: fmt.Println("没有新消息") } // 向通道发送数据 messageChan <- "Hello, World!" // 再次非阻塞地从通道接收数据 select { case msg := <-messageChan: fmt.Println("收到消息:", msg) default: fmt.Println("没有新消息") }上面的代码就是一段典型的无阻塞获取值的代码
在第一个select中 我们没有读取到数据 所以说立即打印没有新消息
在第二个select中 我们读取到了新的数据 所以说会立即打印 hello world
类事件驱动循环 type node struct { heartbeat chan struct{} conn struct { Close func() } } func (n *node) heartbeatDetect() { // 创建一个重置的定时器 timeoutDuration := 3 * time.Second heartbeatTimer := time.NewTimer(timeoutDuration) for { select { case <-n.heartbeat: // 收到心跳信号,重置定时器 if !heartbeatTimer.Stop() { <-heartbeatTimer.C } heartbeatTimer.Reset(timeoutDuration) case <-heartbeatTimer.C: // 心跳超时,关闭连接 n.conn.Close() return } } }在这个代码中 heartbeatDetect方法属于node结构体 node具有一个heartbeat通道 用于接收心跳信号 以及一个conn字段模拟网络连接 其中包含一个Close方法来关闭连接
函数首先创建了一个心跳定时器heartbeatTimer 用于跟踪心跳的超时
在for循环中 select语句监听心跳通道和定时器的通道
当心跳信号到达(通过n.heartbeat通道接收到) 该函数会尝试停止定时器 并且如果通道中有未处理的超时事件 则将其清除 以避免收到虚假的超时 然后它会重置定时器 开始新一轮的心跳等待
如果在规定时间内没有收到心跳(定时器的通道heartbeatTimer.C发出信号) 则会执行关闭连接的逻辑 并退出函数
带优先级的任务队列 highPriority := make(chan string, 5) // 高优先级任务队列 lowPriority := make(chan string, 5) // 低优先级任务队列 // 添加任务到不同优先级的队列 go func() { highPriority <- "紧急任务" lowPriority <- "一般任务" highPriority <- "非常紧急任务" }() // 处理任务 for { select { case task := <-highPriority: // 首选处理高优先级任务 fmt.Println("处理高优先级任务:", task) continue // 继续下一个循环迭代,确保再次检查高优先级队列 default: } select { case task := <-highPriority: // 再次检查高优先级任务队列,确保没有遗漏 fmt.Println("处理高优先级任务:", task) case task := <-lowPriority: // 如果没有高优先级任务,则处理低优先级任务 fmt.Println("处理低优先级任务:", task) } }我们可以使用多次select的方式来实现一个伪优先级任务队列
【Go语言分析selectcase】由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【Go语言分析selectcase】”
上一篇
代码随想录96.不同的二叉搜索树
下一篇
物理结构设计要点