逐步学习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设计的。

这张图是参考大佬Dravenss画的

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"。

file


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在高并发的情况下,出现饿死(被忽略)的情况。

file


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。

file


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

file


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

file


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"

file

file

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编程网 

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

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

相关文章

Gparted工具 初始化磁盘

Gparted工具 初始化磁盘 1、安装 没有此工具请先安装&#xff1a; yum install epel-release yum install gparted yum install yum-utils git gnome-common gcc-c yum-builddep gparted 2、打开Gparted工具&#xff0c;初始化磁盘 使用具有root权限的普通用户打开gparted&…

HWOD:整型数组排序

一、知识点 while(1){}表示永久循环 使用break结束循环 二、题目 1、描述 输入整型数组和排序标识&#xff0c;对其元素按照升序或降序进行排序 2、数据范围 1<n<1000 0<val<100000 3、输入 第一行输入数组元素个数 第二行输入待排序的数组&#x…

电商技术揭秘二:电商平台推荐系统的实现与优化

文章目录 一、推荐系统的重要性1.1 提升用户体验1.1.1 个性化推荐增强用户满意度1.1.2 减少用户选择困难 1.2 增加销售额1.2.1 促进交叉销售和捆绑销售1.2.2 提高用户购买转化率 1.3 数据分析与用户行为理解1.3.1 挖掘用户偏好和购买习惯1.3.2 为产品开发和库存管理提供数据支持…

npm ERR! code CERT_HAS_EXPIRED 淘宝镜像失效

近期vue安装失败&#xff0c;具体如下&#xff1a; 1.先npm cache clean --force 再下载 插件后缀加上 --legacy-peer-deps 2.certificate has expired npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.o…

Windows安装禅道系统结合Cpolar实现公网访问内网BUG管理服务

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 前言 BUG管理软件,作为软件测试工程师的必备工具之一。在…

943: 顺序表插入操作的实现

学习版 【C语言】 需要扩充数组 【C】 #include <iostream> #include <vector> #include <algorithm> using namespace std; class MyLinkedList { public:struct LinkedNode{int val;LinkedNode* next;LinkedNode(int x) :val(x), next(NULL) {}};MyLin…

TypeScript语法快速上手

TypeScript语法 对比ts编译器类型注解新增类型数组自定义类型注解函数类型对象类型元组类型类型推断枚举类型 对比 最大区别&#xff1a;ts能在编译时就能发现类型错误的问题&#xff0c;而js只有在系统运行时再通过异常中断来发现 ts的底层仍是js&#xff0c;但ts能够有效减少…

代码随想录算法训练营第二十七天|131.分割回文串、93.复原IP地址

文档链接&#xff1a;https://programmercarl.com/ LeetCode131.分割回文串 题目链接&#xff1a;https://leetcode.cn/problems/palindrome-partitioning/ 思路&#xff1a;把回溯的树画出来就好很多。startIndex用来控制切割的位置 例如对于字符串abcdef&#xff1a; 组…

golang语言系列:Authentication、OAuth、JWT 认证策略

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 golang语言系列 文章&#xff0c;主要对编程通用技能 Authentication、OAuth、JWT 认证策略 进行学习 1.Basic Authentication认证 每个请求都需要将 用户名密码 进行base64编码后&#xff0c;放在请求头的Aut…

html基础:颜色的 5 种表示方法(最全!)

你好&#xff0c;我是云桃桃。一个希望帮助更多朋友快速入门 WEB 前端的程序媛&#xff0c;大专生&#xff0c;2年时间从1800到月入过万&#xff0c;工作5年买房。 分享成长心得。 HTML 颜色在网页设计中扮演着重要角色&#xff0c;给网页增加颜色可以增强用户体验&#xff0c;…

AI行业买英伟达GPU,花的钱比赚的多17倍

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 有人却表示「很合理」。 搞 AI 大模型&#xff0c;实在太烧钱了。 我们知道&…

SpringBoot mybatis-starter解析

mybatis-starter使用指南 自动检测工程中的DataSource创建并注册SqlSessionFactory实例创建并注册SqlSessionTemplate实例自动扫描mappers mybatis-starter原理解析 注解类引入原理 查看对应的autoconfigure包 MybatisLanguageDriverAutoConfiguration 主要是协助使用注解来…

图像处理环境配置opencv-python

下载python&#xff0c;配置pip使用清华源下载镜像&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 切换到python目录下&#xff0c;右击cmd&#xff0c;执行pip升级指令: python -m pip install --upgrade pip 下载opencv&#x…

JavaEE SSM框架学习——MacOS Eclipse环境搭建

MacOS环境搭建 安装Homebrew Homebrew是一个包管理器&#xff0c;我们可以通过它来安装许多软件 首先打开Homebrew中文官网(brew.sh/zh-cn) 如图所示&#xff0c;复制下面那行命令到你的Macbook终端 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Ho…

针对 qt的sqlite加密数据库sqlitecipher插件QtCipherSqlitePlugin

&#x1f482; 个人主页:pp不会算法^ v ^ &#x1f91f; 版权: 本文由【pp不会算法v】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 文章目录 简介编译安装使用可视化工具查看完结 简介 在客户端存储…

太绝了!34张思维导图搞定Web安全知识架构

推荐关注 越来越多的朋友开始了解网络安全&#xff0c;但是不知道从哪里下手&#xff0c;网上的资源也很散乱&#xff0c;没有头绪&#xff0c;经常做无用功。 今天给大家分享几份网络安全思维导图&#xff0c;非常适合零基础入门或想进一步学习的小伙伴。 注&#xff1a;文…

leetcode 不同路径

62. 不同路径 问题描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的…

【数据处理包Pandas】DataFrame数据选择的基本方法

目录 一、选择行/列&#xff08;一&#xff09;读取文件&#xff08;二&#xff09;选择行&#xff08;三&#xff09;选择列&#xff08;四&#xff09;选择多行多列 二、带条件筛选&#xff08;一&#xff09;startswith()方法&#xff08;二&#xff09;mean()方法&#xff…

本地运行github上下载的项目--接Git入门篇

1.了解项目 这是一个基于Spring Boot 和 Mybatis Plus 构建的Java项目&#xff0c;很经典的外卖项目&#xff0c;参考b站的黑马瑞吉外卖。 2.构建项目 SpringBoot项目&#xff0c;首先下载一些常见的项目要求的组件。然后配置如下&#xff1a; 看README&#xff0c;在阅读该…

如何查找局域网内连接设备的IP地址?

如何查找局域网内连接设备的IP地址? 第一种方法:通过CMD指令 在电脑开始菜单中找到运行,点击打开,输入CMD,然后回车,如下图所示, 如下图所示,输入ipconfig/all,然后回车, 如下图所示,此时会扫描出所有的连接设备的IP地址、MAC地址以及网关等信息, 这时候在cmd命令窗…