在Go语言中,select语句是处理并发编程的核心工具之一。它让我们能够优雅地管理多个通道操作,实现高效的并发控制。

1. Select 语句基础

1.1 Select 的基本语法

package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)// 启动两个goroutine发送数据go func() {time.Sleep(2 * time.Second)ch1 <- "来自通道1的数据"}()go func() {time.Sleep(1 * time.Second)ch2 <- "来自通道2的数据"}()// 使用select等待任一通道有数据select {case msg1 := <-ch1:fmt.Println("收到:", msg1)case msg2 := <-ch2:fmt.Println("收到:", msg2)}
}

1.2 Select 的工作原理

  • select会同时监听所有case中的通道操作
  • 哪个通道先准备好,就执行对应的case
  • 如果多个通道同时准备好,随机选择一个执行
  • 所有case都阻塞时,select也会阻塞

2. Select 的高级用法

2.1 超时控制

func withTimeout() {ch := make(chan string)go func() {// 模拟耗时操作time.Sleep(3 * time.Second)ch <- "操作完成"}()select {case result := <-ch:fmt.Println("成功:", result)case <-time.After(2 * time.Second):fmt.Println("操作超时")}
}

2.2 默认情况(default)

func nonBlocking() {ch := make(chan string, 1)// 尝试读取,不阻塞select {case msg := <-ch:fmt.Println("收到:", msg)default:fmt.Println("通道为空,不等待")}// 尝试写入,不阻塞select {case ch <- "数据":fmt.Println("数据写入成功")default:fmt.Println("通道已满,写入失败")}
}

2.3 结合for循环

func continuousProcessing() {ch1 := make(chan string)ch2 := make(chan string)go func() {for i := 1; i <= 5; i++ {ch1 <- fmt.Sprintf("消息%d", i)time.Sleep(time.Second)}close(ch1)}()go func() {for i := 1; i <= 3; i++ {ch2 <- fmt.Sprintf("通知%d", i)time.Sleep(2 * time.Second)}close(ch2)}()// 持续监听两个通道for {select {case msg, ok := <-ch1:if !ok {ch1 = nil // 关闭后设为nil,不再监听fmt.Println("通道1已关闭")} else {fmt.Println("处理:", msg)}case notify, ok := <-ch2:if !ok {ch2 = nilfmt.Println("通道2已关闭")} else {fmt.Println("通知:", notify)}}// 当两个通道都关闭时退出if ch1 == nil && ch2 == nil {break}}
}

3. 并发控制模式

3.1 信号量模式

func semaphore() {const maxWorkers = 3semaphore := make(chan struct{}, maxWorkers)results := make(chan string, 10)tasks := []string{"任务1", "任务2", "任务3", "任务4", "任务5"}// 启动工作goroutinefor _, task := range tasks {go func(t string) {semaphore <- struct{}{} // 获取信号量// 模拟工作time.Sleep(time.Second)results <- fmt.Sprintf("完成: %s", t)<-semaphore // 释放信号量}(task)}// 收集结果for i := 0; i < len(tasks); i++ {fmt.Println(<-results)}
}

3.2 超时与取消

func timeoutAndCancel() {ch := make(chan string)ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()go func() {// 模拟长时间操作time.Sleep(5 * time.Second)ch <- "操作完成"}()select {case result := <-ch:fmt.Println("结果:", result)case <-ctx.Done():fmt.Println("操作被取消:", ctx.Err())}
}

3.3 多路复用

func multiplexing() {ch1 := make(chan string)ch2 := make(chan string)ch3 := make(chan string)// 模拟不同来源的数据go func() {for i := 1; i <= 3; i++ {ch1 <- fmt.Sprintf("用户%d上线", i)time.Sleep(800 * time.Millisecond)}close(ch1)}()go func() {for i := 1; i <= 2; i++ {ch2 <- fmt.Sprintf("订单%d创建", i)time.Sleep(1200 * time.Millisecond)}close(ch2)}()go func() {for i := 1; i <= 4; i++ {ch3 <- fmt.Sprintf("日志%d记录", i)time.Sleep(500 * time.Millisecond)}close(ch3)}()// 统一处理所有事件for {select {case msg, ok := <-ch1:if !ok {ch1 = nil} else {fmt.Printf("[用户事件] %s\n", msg)}case msg, ok := <-ch2:if !ok {ch2 = nil} else {fmt.Printf("[订单事件] %s\n", msg)}case msg, ok := <-ch3:if !ok {ch3 = nil} else {fmt.Printf("[日志事件] %s\n", msg)}}if ch1 == nil && ch2 == nil && ch3 == nil {break}}
}

4. 实际应用示例

4.1 健康检查服务

func healthCheckService() {checkInterval := time.Tick(5 * time.Second)forceCheck := make(chan struct{})stop := make(chan struct{})go func() {for {select {case <-checkInterval:fmt.Println("定时健康检查...")performHealthCheck()case <-forceCheck:fmt.Println("强制健康检查...")performHealthCheck()case <-stop:fmt.Println("服务停止")return}}}()// 模拟外部触发time.Sleep(3 * time.Second)forceCheck <- struct{}{}time.Sleep(8 * time.Second)stop <- struct{}{}time.Sleep(1 * time.Second)
}func performHealthCheck() {// 模拟健康检查逻辑fmt.Println("  ✓ 数据库连接正常")fmt.Println("  ✓ 缓存服务正常")fmt.Println("  ✓ 网络连接正常")
}

4.2 并发下载器

func concurrentDownloader() {urls := []string{"https://example.com/file1","https://example.com/file2", "https://example.com/file3","https://example.com/file4",}const maxConcurrent = 2semaphore := make(chan struct{}, maxConcurrent)results := make(chan string, len(urls))for _, url := range urls {go func(u string) {semaphore <- struct{}{}defer func() { <-semaphore }()// 模拟下载duration := time.Duration(rand.Intn(3000)+1000) * time.Millisecondtime.Sleep(duration)results <- fmt.Sprintf("下载完成: %s (%v)", u, duration)}(url)}// 收集结果,带超时timeout := time.After(5 * time.Second)completed := 0for completed < len(urls) {select {case result := <-results:fmt.Println(result)completed++case <-timeout:fmt.Println("下载超时")return}}
}

5. 最佳实践

5.1 错误处理

func robustSelect() {ch := make(chan string)errCh := make(chan error)go func() {// 可能出错的操作if rand.Float32() < 0.3 {errCh <- fmt.Errorf("随机错误发生")return}ch <- "正常结果"}()select {case result := <-ch:fmt.Println("成功:", result)case err := <-errCh:fmt.Printf("错误: %v\n", err)case <-time.After(3 * time.Second):fmt.Println("操作超时")}
}

5.2 资源清理

func cleanupExample() {dataCh := make(chan string)done := make(chan struct{})ticker := time.NewTicker(500 * time.Millisecond)defer ticker.Stop()go func() {for range ticker.C {select {case dataCh <- "新数据":fmt.Println("数据发送")case <-done:fmt.Println("发送器停止")returndefault:// 非阻塞发送fmt.Println("缓冲区满,跳过")}}}()// 消费数据time.Sleep(2 * time.Second)close(done) // 通知发送器停止// 继续消费剩余数据for {select {case data, ok := <-dataCh:if !ok {return}fmt.Printf("消费: %s\n", data)default:return // 无数据可消费}}
}

总结

select是Go并发编程的利器,主要用途包括:

  1. 多通道监听:同时处理多个通道操作
  2. 超时控制:避免无限等待
  3. 非阻塞操作:使用default实现非阻塞
  4. 并发协调:结合信号量控制并发度
  5. 优雅退出:配合context实现取消机制

关键要点:

  • select是随机选择就绪的case
  • default用于非阻塞操作
  • 结合time.After实现超时
  • 使用nil通道来停止监听
  • 注意资源清理和错误处理

掌握select的使用,能让你写出更健壮、更高效的并发程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95670.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95670.shtml
英文地址,请注明出处:http://en.pswp.cn/web/95670.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Acme.sh 获取和管理免费 SSL 证书

Acme.sh 是一个开源的 Shell 脚本工具&#xff0c;支持从 Let’s Encrypt 等证书颁发机构获取免费的 SSL/TLS 证书。它支持多种验证方式&#xff0c;并能自动续期证书&#xff0c;适合个人网站或企业使用。 目标 同时支持&#xff0c;主域名和泛域名 安装 Acme.sh获取源码 git …

docker-compose跨节点部署Elasticsearch 9.X集群

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录 前言 一、环境准备 二、遇到的问题与分析 三、配…

【面试场景题】spring应用启动时出现内存溢出怎么排查

文章目录一、定位 OOM 类型二、基础排查&#xff1a;调整 JVM 参数与日志三、堆内存溢出&#xff08;Heap Space&#xff09;排查1. 分析堆转储文件2. 典型场景与解决四、元空间溢出&#xff08;Metaspace&#xff09;排查1. 分析类加载情况2. 典型场景与解决五、直接内存溢出&…

2025年经济学专业女生必考证书指南:打造差异化竞争力

在数字经济快速发展的2025年&#xff0c;经济学专业女生面临着诸多机遇与挑战。单纯的理论知识已经难以满足职场需求&#xff0c;企业更看重解决实际问题的能力&#xff0c;特别是将数据转化为商业洞察的专业技能。各类专业资质认证可以成为系统提升能力的途径之一&#xff0c;…

【CAN通信】AUTOSAR架构下TC3xx芯片是如何将一帧CAN报文接收上来的

目录 前言 正文 1.背景介绍 2.CAN报文硬件原理 3.CAN接收软件实现 3.1. vCan_30_Mcan_Interrupt 3.2. vCan_30_Mcan_RxInterrupt 3.3. vCan_30_Mcan_RxBasicCanHandling 4.总结 前言 在《【CAN通信】AUTOSAR架构下TC3xx芯片是如何将一帧CAN报文发送出去的》一文中我们…

STM32H750 RTC介绍及应用

第十一章 RTC介绍及应用 1. RTC 简介 RTC&#xff08;Real-Time Clock&#xff0c;实时时钟&#xff09;是 STM32H750VBT6 中用于提供日历和时钟功能的低功耗外设&#xff0c;即使主电源关闭&#xff0c;只要 VBAT&#xff08;备份电源&#xff09;供电&#xff0c;RTC 仍能持续…

飞网自适应通信:IPv4 与 IPv6 环境下的高效互联

一、网络连接的难题与飞网的解决方案 在日常生活中&#xff0c;我们常常会碰到这样的场景&#xff1a;在家用手机访问公司电脑里的重要文件&#xff0c;或者远程连接家里的NAS设备查看照片和视频。这些操作都需要设备之间建立起安全又稳定的连接。然而&#xff0c;现实中的网络…

拉格朗日多项式

最近打的几个比赛没意思&#xff0c;不是不会就是不会。不过比赛完后看到别人的WP&#xff0c;感觉受益匪浅。先看一个多项式&#xff1a;当输入Xi时会得到一个Si,要求输入一定数量的xi 来求[c] 当可以输入的x个数与c的个数相同时&#xff0c;可以用矩阵直接求解。&#xff08;…

Vue3 + TypeScript 实现文件拖拽上传

应用效果&#xff1a;实例代码&#xff1a;CommonApplyBasicInfoForm.vue<script setup lang"ts" name"CommonApplyBasicInfoForm"> ...... // 选择文件列表 const selectedFiles ref<FileList | null>(null); // 通过 FormData 对象实现文件…

2025全国大学生数学建模C题保姆级思路模型(持续更新):NIPT 的时点选择与胎儿的异常判定

2025全国大学生数学建模C题保姆级思路模型&#xff08;持续更新&#xff09;&#xff1a;NIPT 的时点选择与胎儿的异常判定&#xff0c;完整持续更新内容见文末名片 胎儿遗传信息检测与临床决策数学建模分析讲义 问题一&#xff1a;Y染色体浓度的影响因素探索——线性回归的“侦…

【笔记】Software Engineering at Google

聚焦核心原则&#xff0c;挑取最让我眼前一亮的实践点&#xff0c;特别是那些能直接启发或解决我当前工作中痛点的部分。0. 序言 最近集中精力速读了关于 ​Google 软件工程实践​ 的诸多资料&#xff08;包括官方出版物、工程博客、技术演讲以及社区讨论&#xff09;。面对 G…

无人机防风技术难点解析

防风防御模块的技术难点主要体现在以下几个方面风场感知与精准建模1.复杂风场的实时感知&#xff1a;风&#xff0c;尤其是近地面的风&#xff0c;常常是无规则、瞬息万变的阵风、湍流或风切变。无人机需要通过各种传感器&#xff08;如空速计、惯性测量单元&#xff08;IMU&am…

HarmonyOS 应用开发深度解析:ArkTS 声明式 UI 与精细化状态管理

好的&#xff0c;请看这篇关于 HarmonyOS 应用开发中声明式 UI 状态管理的技术文章。 HarmonyOS 应用开发深度解析&#xff1a;ArkTS 声明式 UI 与精细化状态管理 引言 随着 HarmonyOS 4、5 的广泛应用和 HarmonyOS NEXT 的发布&#xff0c;基于 API 12 及以上的应用开发已成为…

机器学习入门,第一个MCP示例

前面我们已经搭建了属于自己的AI大模型&#xff1a;详情见 https://blog.csdn.net/hl_java/article/details/150591424?spm1001.2014.3001.5501 近期MCP概念这么火&#xff0c;那么它到底是什么呢&#xff0c;借一个例子为你讲解 第一步&#xff1a;理解MCP的核心价值 MCP (Mo…

flutter 中间组件自适应宽度

使用Flexible IntrinsicWidth Row(children: [Text(第一个text),IntrinsicWidth(child: ConstrainedBox(constraints: BoxConstraints(maxWidth: 200), // 最大宽度限制child: Text(中间的text可能很长也可能很短,overflow: TextOverflow.ellipsis,maxLines: 1,),),),Text(第三…

TDengine 时间函数 DAYOFWEEK 用户手册

DAYOFWEEK 函数使用手册 函数描述 DAYOFWEEK 函数用于返回指定日期是一周中的第几天。该函数遵循标准的星期编号约定&#xff0c;返回值范围为 1-7&#xff0c;其中&#xff1a; 1 星期日 (Sunday)2 星期一 (Monday)3 星期二 (Tuesday)4 星期三 (Wednesday)5 星期四 (T…

【STM32】贪吃蛇 [阶段 3] 增强模块结构(架构优化)

这篇博客是 承接&#xff1a;【项目思维】贪吃蛇&#xff08;嵌入式进阶方向&#xff09;中 聚焦于 &#x1f9f1; 阶段 3&#xff1a;增强模块结构&#xff08;架构优化&#xff09; 中的 菜单系统&#xff08;Menu System&#xff09;&#xff0c;这部分的结构优化可以学到的…

江协科技STM32学习笔记补充之004

STM32 ISP 一键下载电路&#xff08;按功能、逻辑与时序拆解&#xff09;

数据库小册(1)

1. 关系型数据库主要考点关系型数据库&#xff1a;架构索引锁语法理论规范2. 如何设计一个关系型数据库设计即模块划分。数据库最主要的功能是存储我们的数据&#xff0c;所以需要一个存储的文件系统。我们要把涉及到的物流数据提供逻辑的形式给组织和表示出来&#xff0c;这是…

记录收入最高的一次私活 选号网,需要大量卖号的人可能需要,比如游戏脚本批量跑的号

选号网管理后台(上传游戏信息、账号信息、 查看记录) http://124.223.214.5:180/admin1 选号网客户端(PC/H5页面 给客户筛选商品用) http://124.223.214.5:181/ 该在线地址仅供低频率测试&#xff0c;正式使用需要另外部署。 功能不满足可以联系开发者定制 一、项目的由来 …