逐步学习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个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

