逐步学习Go-Select多路复用
概述
这里又有多路复用,但是Go中的这个多路复用不同于网络中的多路复用。在Go里,select用于同时等待多个通信操作(即多个channel的发送或接收操作)。Go中的channel可以参考我的文章:逐步学习Go-并发通道chan(channel)
拆字解释
-
多路:指的是多个channel操作路径。你可以在select块中定义多个case,每个case对应一个channel上的I/O操作(发送或接收)。
-
复用:指的是select的功能,它可以监听多个channel上的事件,并且仅当其中一个channel准备就绪时才会执行相关操作。这样,单个goroutine可以高效地等待多个并发事件而不是单个事件。
复用的是goroutine,一个goroutine使用select可以监听多个信道。
整体来讲:Select就是为channel设计的。

select语法
Go语言中的select关键字功能在概念上与操作系统的select类似,区别在于Go的select是用于goroutine监听多个channel的可读或可写状态。
Go的select允许在channel上进行非阻塞收发,同时当多个channel同时响应时,select会随机执行其中的一个case。
Go的select语句可以包含一个default分支,使得在没有channel准备好时,不会阻塞goroutine,而是执行default分支。
select {case <-ch:println("recieved")case <-time.After(10 * time.Second):println("Timeout")default:printStr = "Hello Select"}
COPY
接下来我们来看场景用例。
在下面的场景测试用例中,我们定义了三个channel: ch1, ch2和ch3。
select只有一个case条件满足
我们创建完成三个channel以后,我们只想ch1发送消息,那么在select三个channel时只有 case <- ch1可以满足,用例执行会输出"Recieved ch1"。

func TestSelect_ShouldRecvChan1_WhenChan1CaseWasFullfilled(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)chans := []chan int{ch1, ch2, ch3}var wg sync.WaitGroupwg.Add(1)go func() {chans[0] <- 1wg.Done()}()wg.Wait()select {case <-ch1:println("Recieved ch1")case <-ch2:println("Recieved ch2")case <-ch3:println("Recieved ch3")case <-time.After(10 * time.Second):println("Timeout")default:}}
COPY
select有多个case条件满足
在这个场景中,我们使用三个goroutine向三个channel都发送了消息,然后等待所有的goroutine执行完成确保3个channel都接收到了消息,那么select会随机选择一个case条件执行(如果自己测试需要多执行几次,因为不止下一次会执行那个case分支)。
如果select中的多个case同时满足,Go语言如何进行选择。Go语言官方文档规定,当多个case都可以运行时,Go会按照"伪随机"的方式来选择一个case执行。这个"伪随机"是指,它不是完全随机的,而是通过一定的算法进行选择,以防止某个channel在高并发的情况下,出现饿死(被忽略)的情况。
func TestSelect_ShouldRandomEnterCaseBranch_WhenAllChannelsCaseWereFullfilled(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)chans := []chan int{ch1, ch2, ch3}var wg sync.WaitGroupwg.Add(3)for i := 0; i < 3; i++ {go func(i int) {chans[i] <- 1wg.Done()}(i)}wg.Wait()select {case <-ch1:println("Recieved ch1")case <-ch2:println("Recieved ch2")case <-ch3:println("Recieved ch3")case <-time.After(10 * time.Second):println("Timeout")default:}}
COPY
select没有条件满足-阻塞(Deadlock)
在这个场景,我们只是创建了3个channel,但是没有向三个channel发送消息,那么执行select时go会panic, 错误信息为:Deadlock。

func TestSelect_ShouldBlock_WhenNoCaseWasFullfilled(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)select {case <-ch1:println("Recieved ch1")case <-ch2:println("Recieved ch2")case <-ch3:println("Recieved ch3")}}
COPY
select没有条件满足-超时
在这个场景中,我们创建了三个channel,然后没有向这三个channel中发送消息,最后我们使用select尝试从三个channel中接收消息,但是我们在case中增加了一个超时检测。
在这个场景中,select会在10秒后执行 case <-time.After(10 * time.Second):分支因为管道条件没有被满足且没有default。

func TestSelect_ShouldTimeout_WhenNoCaseWasFullfilled(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)select {case <-ch1:println("Recieved ch1")case <-ch2:println("Recieved ch2")case <-ch3:println("Recieved ch3")case <-time.After(10 * time.Second):println("Timeout")}}
COPY
select没有条件满足-default
在这个场景中,代码和上面的差别在于我们添加了default分支,添加了default后如果所有case没有条件满足则执行default分支,所以你执行这个用例控制台会打印Default。

func TestSelect_ShouldRunDefaultBranch_WhenNoCaseWasFullfilledAndHasDefaultBranch(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)select {case <-ch1:println("Recieved ch1")case <-ch2:println("Recieved ch2")case <-ch3:println("Recieved ch3")case <-time.After(10 * time.Second):println("Timeout")default:println("Default")}}
COPY
select关闭的channel接收
在这个场景中,我们创建3个channel然后理解关闭,最后使用select来读取三个channel,那么根据关闭后chnanel的定义:channel在关闭后永远都可以读取,那么select 的case条件可以被满足且随机选择一个case分支执行,只是读取到的都是“0”值。
注意:0值这个是根据不同类型而不一样的,而且go是严格类型检查,nil是不通用的
func TestSelect_ShouldRecvZeroValue_WhenSelectFromClosedChannel(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)close(ch1)close(ch2)close(ch3)select {case value := <-ch1:if value == 0 {println("Recieved zero value from ch1")} else {println("Recieved ch1")}case value := <-ch2:if value == 0 {println("Recieved zero value from ch2")} else {println("Recieved ch2")}case value := <-ch3:if value == 0 {println("Recieved zero value from ch3")} else {println("Recieved ch3")}case <-time.After(10 * time.Second):println("Timeout")default:println("Default")}
}
COPY
select 在关闭的channel上发送
在这个场景中,我们向通过select的case分支来向关闭的channel发送数据,根据go channel的定义:会发生panic。
如下截图,我们的UT显示PASS,表示发生了Panic,当然你也可以改变一下把assert.Panics注释掉,直接执行select的,那么你会得到第二张图的结果:"send on closed channel"


func TestSelect_ShouldPanic_WhenSendToClosedChannel(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)ch3 := make(chan int, 1)close(ch1)close(ch2)close(ch3)assert.Panics(t, func() {select {case ch1 <- 1:println("send ch1")case ch2 <- 1:println("send ch2")case ch3 <- 1:println("send ch3")case <-time.After(10 * time.Second):println("Timeout")default:println("Default")}})}
参考: 逐步学习Go-Select多路复用 – FOF编程网
相关文章:
逐步学习Go-Select多路复用
概述 这里又有多路复用,但是Go中的这个多路复用不同于网络中的多路复用。在Go里,select用于同时等待多个通信操作(即多个channel的发送或接收操作)。Go中的channel可以参考我的文章:逐步学习Go-并发通道chan(channel)…...
王道:OJ15
课时15作业 Description 读取10个元素 87 7 60 80 59 34 86 99 21 3,然后建立二叉查找树,排序后输出3 7 21 34 59 60 80 86 87 99,针对有序后的元素,存入一个长度为10的数组中,通过折半查找找到21的下标(…...
【案例·查】数据类型强制转换,方便查询匹配
问题描述: MySQL执行中需要将某种数据类型的表达式显式转换为另一种数据类型,可以使用 SQL 中的cast()来处理 案例: SELECT CAST(9.0 AS decimal) #String化为小数类型SELECT * FROM table_1 WHERE 1888-03-07 CAST(theDate AS DATE) …...
spring boot3自定义注解+拦截器+Redis实现高并发接口限流
⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途 目录 写在前面 内容简介 实现思路 实现步骤 1.自定义限流注解 2.编写限流拦截器 3.注册拦截器 4.接口限流测试 写在前…...
使用certbot为网站启用https
1. 安装certbot客户端 cd /usr/local/bin wget https://dl.eff.org/certbot-auto chmod ax ./certbot-auto 2. 创建目录和配置nginx用于验证域名 mkdir -p /data/www/letsencryptserver {listen 80;server_name ~^(?<subdomain>.).ninvfeng.com;location /.well-known…...
Unity 背包系统中拖拽物体到指定位置或互换位置效果的实现
在Unity中,背包系统是一种常见的游戏系统,可以用于管理和展示玩家所持有的物品、道具或装备。 其中的拖拽功能非常有意思,具体功能就是玩家可以通过拖拽物品图标来移动物品在背包中的位置,或者将物品拖拽到其他位置或界面中&…...
iOS客户端自动化UI自动化airtest+appium从0到1搭建macos+脚本设计demo演示+全网最全最详细保姆级有步骤有图
Android客户端自动化UI自动化airtest从0到1搭建macos脚本设计demo演示全网最全最详细保姆级有步骤有图-CSDN博客 避坑系列-必读: 不要安装iOS-Tagent ,安装appium -这2个性质其实是差不多的都是为了安装wda。注意安装appium最新版本,安装完…...
每周编辑精选|在线运行 Deepmoney 金融大模型、AI 偏好等多个优质数据集上线
目前,AI 领域对金融模型的研究成果大多是基于公共知识进行训练的,但在实际的金融实践中,这些公共知识对于当前市场的可解释性往往严重不足。一个理想的金融大模型应该能够理解新闻或数据事件,并能够即时地从主观和量化两个角度对事…...
C++多重继承与虚继承
多重继承的原理 多重继承(multiple inheritance)是指从多个直接基类中产生派生类的能力。 多重继承的派生类继承了所有父类的属性。 在面向对象的编程中,多重继承意味着一个类可以从多个父类继承属性和方法。 就像你有一杯混合果汁,它是由多种水果榨取…...
请简单介绍一下Shiro框架是什么?Shiro在Java安全领域的主要作用是什么?Shiro主要提供了哪些安全功能?
请简单介绍一下Shiro框架是什么? Shiro框架是一个强大且灵活的开源安全框架,为Java应用程序提供了全面的安全解决方案。它主要用于身份验证、授权、加密和会话管理等功能,可以轻松地集成到任何Java Web应用程序中,并提供了易于理解…...
TouchGFX之Button
TouchGFX中的按钮是一种感应触控事件的控件,能够在按钮被按下/释放时发送回调 代码 #ifndef TOUCHGFX_ABSTRACTBUTTON_HPP #define TOUCHGFX_ABSTRACTBUTTON_HPP #include <touchgfx/Callback.hpp> #include <touchgfx/events/ClickEvent.hpp> #includ…...
计算机组成原理 — 指令系统
指令系统 指令系统指令的概述指令的格式指令的字长取决于 操作数类型和操作种类操作数的类型数据在存储器中的存放方式操作类型 寻址方式指令寻址数据寻址立即寻址直接寻址隐含寻址间接寻址寄存器寻址寄存器间接寻址基址寻址变址寻址堆栈寻址 RISC 和 CISC 技术RISC 即精简指令…...
使用easyYapi生成文档
easyYapi生成文档 背景1.安装配置1.1 介绍1.2 安装1.3 配置1.3.1 Export Postman1.3.2 Export Yapi1.3.3 Export Markdown1.3.4 Export Api1.3.6 常见问题补充 2. java注释规范2.1 接口注释规范2.2 出入参注释规范 3. 特定化支持3.1 必填校验3.2 忽略导出3.3 返回不一致3.4 设置…...
蓝桥杯练习题总结(三)线性dp题(摆花、数字三角形加强版)
目录 一、摆花 思路一: 确定状态: 初始化: 思路二: 确定状态: 初始化: 循环遍历: 状态转移方程: 二、数字三角形加强版 一、摆花 题目描述 小明的花店新开张,为了吸…...
Elasticsearch(15) multi_match的使用
elasticsearch version: 7.10.1 multi_match是Elasticsearch中的一种查询类型,允许在一个或多个字段上执行全文本搜索,并合并各个字段的结果得分。这种查询有助于实现跨多个字段的统一搜索体验。 语法 {"query": {"multi_m…...
nodejs的线程模型和libuv库的基本使用
文章目录 nodejs中集成addon本地代码的回调问题单线程事件驱动模型libuvlibuv基本框架addon中使用libuv代码nodejs中集成addon本地代码的回调问题 在C++的代码中,回调函数是一个基本的代码调用方式。而在我自己的开发实践中,需要在addon这样一个nodejs的本地化模块中实现一个…...
Uni-app/Vue/Js本地模糊查询,匹配所有字段includes和some方法结合使用e
天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.第一步 需要一个数组数据 {"week": "全部","hOutName": null,"weekendPrice": null,"channel": "门市价","hOutId": 98,"cTime": "…...
深度学习pytorch——激活函数损失函数(持续更新)
论生物神经元与神经网络中的神经元联系——为什么使用激活函数? 我们将生物体中的神经元与神经网络中的神经元共同分析。从下图可以看出神经网络中的神经元与生物体中的神经元有很多相似之处,由于只有刺激达到一定的程度人体才可以感受到刺激,…...
《苹果 iOS 应用开发与分发的关键问题解析》
一、背景 解决同事问的问题,来来回回被问好几次相同的问题,然后确认,我觉得不如写个文档 二、非研发人员安装iOS应用方式 TestFlightIPA 文件 对比 TestFlightIPA 文件安装方式TestFlight 是苹果提供的一个 beta 测试平台,开发者…...
爱上数据结构:顺序表和链表
一、线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
windows系统MySQL安装文档
概览:本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容,为学习者提供全面的操作指导。关键要点包括: 解压 :下载完成后解压压缩包,得到MySQL 8.…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)
之前都是使用react-pdf来渲染pdf文件,这次有个需求是要兼容xp环境,xp上chrome最高支持到49,虽然说iframe或者embed都可以实现预览pdf,但为了后续的定制化需求,还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...
LINUX编译vlc
下载 VideoLAN / VLC GitLab 选择最新的发布版本 准备 sudo apt install -y xcb bison sudo apt install -y autopoint sudo apt install -y autoconf automake libtool编译ffmpeg LINUX FFMPEG编译汇总(最简化)_底部的附件列表中】: ffmpeg - lzip…...

