15分钟学 Go 第 29 天:流程控制 - select语句

ops/2024/10/31 23:10:33/

第29天:流程控制 - Select语句

1. 目标

理解Go语言中select语句的使用,以及如何在并发编程中有效地管理多个通道的操作。

2. select语句概述

select语句是Go语言中处理多个通道操作的强大机制。它类似于switch语句,但其用于处理通道事件。通过select,我们可以等待多个通道中的任意一个变得可用,从而实现高效的并发控制。

2.1 语法

select {
case <-ch1:// ch1 可读取
case msg := <-ch2:// 从 ch2 可读取消息 msg
case ch3 <- msg:// 向 ch3 发送 msg
default:// 如果没有任何通道准备好
}

2.2 关键点

  • 每个case必须是一个通道操作。
  • select会随机选择一个可用的case,如果多个case同时可用,则会随机选择一个执行。
  • 若所有通道都不可用,且存在default分支,则执行default分支。
  • select语句是阻塞的,直到有case可以执行。

3. 使用场景

  • 处理多种通道: 允许并发地处理多个通道。
  • 超时控制: 在没有通道可用时,设置超时机制。
  • 任务结果收集: 收集多个并行任务的结果。

4. 示例代码

4.1 基本示例

下面是一个简单的select语句示例,展示如何从多个通道中接收数据。

package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)go func() {time.Sleep(1 * time.Second)ch1 <- "来自通道1的消息"}()go func() {time.Sleep(2 * time.Second)ch2 <- "来自通道2的消息"}()for i := 0; i < 2; i++ {select {case msg1 := <-ch1:fmt.Println("接收到:", msg1)case msg2 := <-ch2:fmt.Println("接收到:", msg2)}}
}

4.2 运行流程图

以下是基本示例的运行流程图:

开始|v
创建通道 ch1 和 ch2|v
启动 goroutine 发送消息到 ch1|v
启动 goroutine 发送消息到 ch2|v
进入 for 循环|v
选择消息:|---------  ch1 有消息  -----------> 打印来自通道1的消息,继续|v|---------  ch2 有消息  -----------> 打印来自通道2的消息,继续|v
结束

5. 高级使用

5.1 超时控制

可以通过使用time.After函数来设置超时控制。

package mainimport ("fmt""time"
)func main() {ch := make(chan string)go func() {time.Sleep(2 * time.Second)ch <- "消息来自通道"}()select {case msg := <-ch:fmt.Println("接收到:", msg)case <-time.After(1 * time.Second):fmt.Println("超时,没有接收到消息")}
}

5.2 运行流程图

开始|v
创建通道 ch|v
启动 goroutine,2秒后发送消息到 ch|v
进入 select 语句:|+---+------------------+|   |                  |v   v                  v
接收到消息     超时 1 秒     <--- 超时|   |                  |+---+------------------+|v
结束

5.3 选择多个通道的消息

在处理多个通道并选择第一个可用的情况下,可以更有效地使用select

package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)go func() {time.Sleep(1 * time.Second)ch1 <- "通道1的消息"}()go func() {time.Sleep(2 * time.Second)ch2 <- "通道2的消息"}()for i := 0; i < 2; i++ {select {case msg1 := <-ch1:fmt.Println("接收到:", msg1)case msg2 := <-ch2:fmt.Println("接收到:", msg2)}}
}

运行流程图:

开始|v
创建通道 ch1 和 ch2|v
启动 goroutine 发送消息到 ch1|v
启动 goroutine 发送消息到 ch2|v
进入 for 循环|v
选择消息:|---------  ch1 有消息  -----------> 打印来自通道1的消息,继续|v|---------  ch2 有消息  -----------> 打印来自通道2的消息,继续|v
结束

6. 理解select的使用细节

6.1 战胜竞争条件

在并发编程中,需要小心避免竞争条件,尤其是在多个Goroutine同时访问共享资源时。select可以帮助我们合理安排资源使用。

6.2 哨兵模式

使用select可以实现哨兵模式,允许协程在通道中发送特定的结束信号。

package mainimport ("fmt""time"
)func main() {ch := make(chan string)done := make(chan bool)go func() {for {select {case msg := <-ch:fmt.Println("接收到:", msg)case <-done:fmt.Println("接收任务完成,退出")return}}}()// 发送消息ch <- "Hello, Go!"time.Sleep(1 * time.Second)// 发送结束信号done <- true
}

流程图:

开始|v
创建通道 ch 和 done|v
启动 goroutine 监听 ch 和 done|v
发送消息到 ch|v
等待 1 秒|v
发送结束信号到 done|v------> goroutine 结束

7. 总结

通过本节内容,我们深入理解了Go语言的select语句及其在并发编程中的应用。这一机制为我们提供了灵活的方式来处理多个通道,提高程序的效率和响应性。

7.1 重要概念回顾

  • select语句用于监听多条通道的事件。
  • 在多个通道都可用时,select随机选择其中之一执行。
  • 可以通过time.After实现超时控制。
  • 哨兵模式能有效管理协程的结束信号。

8. 练习

  1. 写一个程序,使用select语句从两个通道中接收字符串,并记录哪个通道先接收到消息。
  2. 实现一个超时功能,如果在3秒内没有消息从通道中接收,则打印"超时"。
  3. 使用哨兵模式,当接收到特定消息时,结束程序。

通过以上内容,相信你对Go语言中的select语句有了全面的理解。如果在学习过程中有任何问题,欢迎随时讨论!


怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!


http://www.ppmy.cn/ops/129962.html

相关文章

WordPress插件 Lightsns主题专版-AI内容生成 V1.6 AI驱动的内容创作工具

Lightsns主题专版-AI内容生成插件详细介绍 插件概述 Lightsns主题专版-AI内容生成插件是一款为WordPress平台Lightsns主题设计的插件&#xff0c;旨在通过集成AI技术&#xff0c;简化内容创作过程。该插件由智狐联创提供AI技术支持&#xff0c;允许用户在发表文章页面增加一个…

(续)残差的尺度化方法

内容来源 线性回归分析导论 原书第5版 机械工业出版社 本篇讲PRESS残差与R-学生残差 PRESS残差&#xff08;也称剔除残差&#xff09; 剔除法 寻找离群值的另一个思路是剔除法 即剔除第 i i i 个点&#xff0c;基于剩下的 n − 1 n-1 n−1 个观测值生成回归模型 再用这个…

【问题解决】pnpm : 无法将“pnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

今天配置完poetry环境变量之后pnpm不能用了 具体报错 pnpm : 无法将“pnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。 所在位置 行:1 字符: 1pnpm run dev~~~~ Ca…

零跑汽车嵌入式面试题汇总及参考答案

C++ 的三大特性是什么? C++ 的三大特性分别是封装、继承和多态。 封装 概念:封装是把数据和操作数据的函数绑定在一起,对数据的访问进行限制。通过将数据成员声明为私有或保护,只允许通过公共的成员函数来访问和修改数据,从而隐藏了类的内部实现细节。这有助于提高代码的安…

安装使用docker harbor并推送镜像到仓库

1.概要 通过上一章节的讲解&#xff0c;我们基本了解了docker的操作命令&#xff0c;在文章的最后我们成功的推送一个镜像到DockerHub的镜像仓库。从流程上说&#xff0c;操作过程可以说很完美&#xff0c;但是整个推送过程消耗的时间太长&#xff0c;我们消耗了大量时间在访问…

spring中bean的四种创建方式

本次分享一下spring中bean的四种创建方式 1. 方式一:普通配置 <bean id"myBean" class"cn.cjc.MyBean"> </bean>2. 方式二:集成静态工厂 // 准备静态工厂 public class CarFactory { //静态方法&#xff0c;返回一个对象 public static Car…

webSocket简单接收发送案例

pom添加&#xff1a; <!-- websocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency> socket服务实现 import com.alibaba.fastjson.JSON; impo…