逐步学习Go-Select多路复用

news2024/11/24 19:07:49

概述

这里又有多路复用,但是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.WaitGroup
    wg.Add(1)

    go func() {
        chans[0] <- 1
        wg.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.WaitGroup
    wg.Add(3)

    for i := 0; i < 3; i++ {
        go func(i int) {
            chans[i] <- 1
            wg.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.coloradmin.cn/o/1553143.html

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

相关文章

OpenHarmony中的LLDB高性能调试器

概述 LLDB&#xff08;Low Lever Debugger&#xff09;是新一代高性能调试器。详细说明参考 LLDB官方文档 。 当前OpenHarmony中的LLDB工具是在 llvm15.0.4 基础上适配演进出来的工具&#xff0c;是HUAWEI DevEco Studio工具中默认的调试器&#xff0c;支持调试C和C应用。 工…

Codeforces Round 841 (Div. 2) C. Even Subarrays

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; co…

基于SVM的PLOSAR图像分类

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

|行业洞察·汽车|《2024新能源汽车行业及营销趋势报告-20页》

报告的主要内容解读&#xff1a; 新能源汽车行业概述及品牌分布&#xff1a; 近年来&#xff0c;中国新能源汽车销量增速高&#xff0c;市场占有率快速提升&#xff0c;成为汽车行业的重要增量。新能源汽车消费者趋向年轻化、女性化和高端化&#xff0c;对高科技、新体验有较高…

zedboard+AD9361 运行 open WiFi

先到github上下载img&#xff0c;网页链接如下&#xff1a; https://github.com/open-sdr/openwifi?tabreadme-ov-file 打开网页后下载 openwifi img 用win32 Disk lmager 把文件写入到SD卡中&#xff0c;这一步操作会把SD卡重新清空&#xff0c;注意保存数据。这个软件我会…

铸铁平台精度是怎么保持的——北重企业

铸铁平台的精度主要通过以下几个方面来保持&#xff1a; 材料选择&#xff1a;铸铁平台通常采用高强度、高硬度的铸铁材料&#xff0c;如HT250。这种材料具有优异的机械性能和耐磨性&#xff0c;能够提供稳固的支撑和保持平台的形状稳定。 加工工艺&#xff1a;铸铁平台在制造…

Freemarker环境搭建快速入门

Freemarker环境搭建&快速入门 测试工程搭建快速入门Freemarker指令语法基础语法种类集合指令&#xff08;List和Map&#xff09;if指令运算符空置处理内建函数 输出静态化文件 测试工程搭建 创建一个freemarker-demo 的测试工程专门用于freemarker的功能测试与模板的测试。…

【图论】【割点】【C++算法】928. 尽量减少恶意软件的传播 II

作者推荐 视频算法专题 涉及知识点 图论 割点 LeetCode928. 尽量减少恶意软件的传播 II 给定一个由 n 个节点组成的网络&#xff0c;用 n x n 个邻接矩阵 graph 表示。在节点网络中&#xff0c;只有当 graph[i][j] 1 时&#xff0c;节点 i 能够直接连接到另一个节点 j。 …

八大技术趋势案例(云计算大数据)

科技巨变,未来已来,八大技术趋势引领数字化时代。信息技术的迅猛发展,深刻改变了我们的生活、工作和生产方式。人工智能、物联网、云计算、大数据、虚拟现实、增强现实、区块链、量子计算等新兴技术在各行各业得到广泛应用,为各个领域带来了新的活力和变革。 为了更好地了解…

<el-table>设置一列为固定字段,其他列为循环生成

<el-table :data"tableData" style"width: 100%"><el-table-columnprop"name"label"固定字段名":formatter"formatter"></el-table-column><el-table-columnv-for"(item, index) in wordsColumns…

【Golang入门教程】Go语言变量的初始化

文章目录 强烈推荐引言举例多个变量同时赋值总结强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站:人工智能 推荐一个个人工作&#xff0c;日常中比较常…

学透Spring Boot — [二] Spring 和 Spring Boot的比较

欢迎关注我们的专栏 学透 Spring Boot 一、创建一个简单Web应用 本篇文章&#xff0c;我们将会比较 Spring 框架和 Spring Boot 的区别。 什么是 Spring? 也许你在项目中已经可以很熟练的使用 Spring 了&#xff0c;但是当被问到这个问题时&#xff0c;会不会犹豫一下&#…

[flink 实时流基础系列]揭开flink的什么面纱基础一

Apache Flink 是一个框架和分布式处理引擎&#xff0c;用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行&#xff0c;并能以内存速度和任意规模进行计算。 文章目录 0. 处理无界和有界数据无界流有界流 1. Flink程序和数据流图2. 为什么一定要…

冒泡排序(六大排序)

冒泡排序 冒泡排序的特性总结&#xff1a; 1. 冒泡排序是一种非常容易理解的排序 2. 时间复杂度&#xff1a;O(N^2) 3. 空间复杂度&#xff1a;O(1) 4. 稳定性&#xff1a;稳定 动图分析&#xff1a; 代码实现&#xff1a; Swap(int*p1,int*p2) {int tmp *p1;*p1*p2…

基于SSM学生信息管理系统

采用技术 基于SSM学生信息管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 总体功能设计 登录页面 后台首页 学生信息页面 添加学生用户 编辑…

Java八股文(数据结构)

Java八股文の数据结构 数据结构 数据结构 请解释以下数据结构的概念&#xff1a;链表、栈、队列和树。 链表是一种线性数据结构&#xff0c;由节点组成&#xff0c;每个节点包含了指向下一个节点的指针&#xff1b; 栈是一种后进先出&#xff08;LIFO&#xff09;的数据结构&a…

【教学类-35-20】20240328 中4班描字帖(学号+姓名 A4竖版2份 横面)+裁剪纸条+插入式纸盒

作品展示 背景需求&#xff1a; 整理仓库&#xff0c;找到之前打印的另外一套灰色版的学号字体&#xff08;和2月20日那份一模一样&#xff09; 【教学类-35-19】20240117 中4班描字帖&#xff08;学号姓名 A4竖版2份 横面&#xff09;-CSDN博客文章浏览阅读571次&#xff0c;…

Mysql数据库-DQL查询

Mysql数据库-DQL基本查询 1 DQL基本查询1.1 基础查询1.2 WHERE子句1&#xff09;算术运算符2&#xff09;逻辑运算符3&#xff09;比较运算符A&#xff09;BETWEEN... AND ...B&#xff09;IN(列表)C&#xff09;NULL值判断 4&#xff09;综合练习 2 DQL高级查询2.1 LIKE 模糊查…

SAP-CO主数据之统计指标创建-<KK01>

公告&#xff1a;周一至周五每日一更&#xff0c;周六日存稿&#xff0c;请您点“关注”和“在看”&#xff0c;后续推送的时候不至于看不到每日更新内容&#xff0c;感谢。 目录 一、背景&#xff1a; 成本中心主数据创建&#xff1a;传送门 成本要素主数据创建&#xff1…

人工智能(pytorch)搭建模型26-基于pytorch搭建胶囊模型(CapsNet)的实践,CapsNet模型结构介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型26-基于pytorch搭建胶囊模型(CapsNet)的实践&#xff0c;CapsNet模型结构介绍。CapsNet&#xff08;Capsule Network&#xff09;是一种创新的深度学习模型&#xff0c;由计算机科学家Geo…