Go select 语句使用场景

1. select介绍

select 是 Go 语言中的一种控制结构,用于在多个通信操作中选择一个可执行的操作。它可以协调多个 channel 的读写操作,使得我们能够在多个 channel 中进行非阻塞的数据传输、同步和控制。

基本语法:

select {case communication clause  :statement(s);case communication clause  :statement(s);/* 你可以定义任意数量的 case */default : /* 可选 */statement(s);
}

如果多个 case 都可以运行,select 会随机公平地选出一个执行。如果没有 case 可以运行,它要么阻塞(等待 case),要么执行default子句。

2. select 语句的常用使用场景:

  • 等待多个通道的消息(多路复用)

    当我们需要等待多个通道的消息时,使用 select 语句可以非常方便地等待这些通道中的任意一个通道有消息到达,从而避免了使用多个goroutine进行同步和等待。

  • 超时等待通道消息

    当我们需要在一段时间内等待某个通道有消息到达时,使用 select 语句可以与 time 包结合使用实现定时等待。

  • 在通道上进行非阻塞读写

    在使用通道进行读写时,如果通道没有数据,读操作或写操作将会阻塞。但是使用 select 语句结合 default 分支可以实现非阻塞读写,从而避免了死锁或死循环等问题。

因此,select 的主要作用是在处理多个通道时提供了一种高效且易于使用的机制,简化了多个 goroutine 的同步和等待,使程序更加可读、高效和可靠。

3. 代码示例:

代码1:

package mainimport ("fmt""time"
)func main() {chan1 := make(chan int)chan2 := make(chan int)go func() {chan1 <- 1time.Sleep(5 * time.Second)}()go func() {chan2 <- 1time.Sleep(5 * time.Second)}()select {case <-chan1:fmt.Println("chan1")case <-chan2:fmt.Println("chan2")default:fmt.Println("default")}fmt.Println("main exit")
}

输出结果为:

可能会出现三种结果:

chan1
main exit
chan2
main exit
default
main exit

select 中的 case 执行顺序是随机的,如果某个 case 中的 channel 已经 ready,那么就会执行相应的语句并退 出 select 流程,如果所有 case 中的 channel 都未 ready,那么就会执行 default 中的语句然后退出 select 流程。

由于启动的协程和 select 语句并不能保证执行的顺序,所以也有可能 select 执行时协程还未向channel中写入数据,所以 select 直接执行 default 语句并退出。因此,程序有可能产生三种输出

代码2:

package mainimport ("fmt"
)func main() {chan1 := make(chan int)chan2 := make(chan int)go func() {close(chan1)}()go func() {close(chan2)}()select {case <- chan1:fmt.Println("chan1")case <- chan2:fmt.Println("chan2")}fmt.Println("main exit.")
}

select 会随机检测各 case 语句中 channel 是否 ready,注意已关闭的 channel 也是可读的,所以上述程序中select 不会阻塞,具体执行哪个 case 语句是随机的。

代码3:

package mainfunc main() {select {}
}

对于空的 select 语句,程序会被阻塞,确切的说是当前协程被阻塞,同时 Go 自带死锁检测机制,当发现当前协程再也没有机会被唤醒时,则会发生 panic。所以上述程序会 panic。

定时器实现定时任务的执行代码:

package mainimport ("fmt""time"
)func main() {fmt.Println("定时任务开始")// 创建一个每秒触发一次的定时器ticker := time.NewTicker(1 * time.Second)done := make(chan bool)go func() {for {select {case <-ticker.C:fmt.Println("执行定时任务")case <-done:ticker.Stop()return}}}()// 等待5秒time.Sleep(5 * time.Second)done <- truefmt.Println("定时任务结束")
}

结果:

定时任务开始
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
定时任务结束

超时退出实现代码:

package mainimport ("fmt""time"
)func main() {timeout := 5 * time.Seconddone := make(chan bool)go func() {// 模拟耗时操作time.Sleep(2 * time.Second)done <- true}()select {case <-done:fmt.Println("Task completed successfully.")case <-time.After(timeout):fmt.Println("Timeout! The operation took too long.")}
}

或:

package mainimport ("context""fmt""time"
)func main() {timeout := 5 * time.Secondctx, cancel := context.WithTimeout(context.Background(), timeout)defer cancel()done := make(chan bool)go func() {// 模拟耗时操作time.Sleep(2 * time.Second)done <- true}()select {case <-done:fmt.Println("Task completed successfully.")case <-ctx.Done():fmt.Println("Timeout! The operation took too long.")}
}

4. 总结

  • select 语句中除 default 外,每个 case 操作一个channel,要么读要么写

  • select语句中除 default 外,各 case 执行顺序是随机的

  • select 语句中如果没有 default 语句,则会阻塞等待任一 case

  • select 语句中读操作要判断是否成功读取,关闭的 channel 也可以读取

select在 Go 语言的源代码中不存在对应的结构体,只是定义了一个 runtime.scase 结构体(在src/runtime/select.go)表示每个 case 语句(包含defaut):

// Select case descriptor.
// Known to compiler.
// Changes here must also be made in src/cmd/compile/internal/walk/select.go's scasetype.
type scase struct {c    *hchan         // chanelem unsafe.Pointer // data element
}

因为所有的非 default 的 case 基本都要求是对Channel的读写操作,所以 runtime.scase 结构体中也包含一个 runtime.hchan 类型的字段存储 case 中使用的 Channel,另一个字段 elem 指向 case 条件包含的数据的指针,如 case ch1 <- 1,则 elem 指向常量1.

编译器会对select有不同的case的情况进行优化以提高性能。首先,编译器对select没有case、有单case和单case+default的情况进行单独处理,这些处理或者直接调用运行时函数,或者直接转成对channel的操作,或者以非阻塞的方式访问channel,多种灵活的处理方式能够提高性能,尤其是避免对channel的加锁。

对最常出现的select有多case的情况,会调用runtime.selectgo()函数来获取执行case的索引

func selectgo(cas0 *scase, order0 *uint16, pc0 *uintptr, nsends, nrecvs int, block bool) (int, bool)

selectgo函数内部逻辑:

  1. 使用fastrandn算法把scases数组的索引重新编排顺序。

  2. 根据新的索引顺序对hchan进行堆排序来获取case的锁定顺序。(保证 n log n 时间和恒定的堆栈占用空间)

  3. 锁定所有channel。

  4. 遍历所有channel,判断是否有可读或者可写的,如果有,解锁channel,返回对应数据。

  5. 否则,判断有没有default,如果有,解锁channel,返回default对应scase。

  6. 否则,把当前groutian添加到所有channel的等待队列里,解锁所有channel,等待被唤醒。

  7. 被唤醒后,再次锁定所有channel

  8. 遍历所有channel,把g从channel等待队列中移除,并找到可操作的channel

  9. 如果对应的scase不为空,直接返回对应的值

  10. 否则循环此过程

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

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

相关文章

Python中的函数式编程特性深入探讨

Python中的函数式编程特性深入探讨 Python,作为一种多范式编程语言,不仅支持面向对象编程(OOP),同样也对函数式编程(FP)提供了良好的支持。函数式编程是一种编程范式,它将计算机程序看作是一系列函数的求值,并避免使用可变状态和共享状态。本文将深入探讨Python中的函…

数据赋能(112)——体系:监控数据采集——影响因素、直接作用、主要特征

影响因素 影响监控数据采集的主要影响因素如下&#xff1a; 采样率&#xff1a;采样率是指采集数据的频率。采样率决定了监控的实时性与精确度&#xff0c;一般来说&#xff0c;采样率越高&#xff0c;监控的实时性就越高&#xff0c;精确度也越高。但是&#xff0c;过高的采…

2024 cicsn SuperHeap

文章目录 参考沙箱存在protobuf逆向buy_booksee_bookreturn_bookedit_booksearch_book 思路exp 参考 https://hakuya.work/post/7 https://akaieurus.github.io/2024/05/20/2024%E5%9B%BD%E8%B5%9B%E5%88%9D%E8%B5%9Bpwn-wp/#SuperHeap https://blog.csdn.net/m0_63437215/art…

TSINGSEE青犀视频汇聚机房动环智能监控方案,提升机房安全稳定性

一、背景需求 在当今信息化时代&#xff0c;机房作为数据中心的核心设施&#xff0c;承载着重要的网络设备和数据存储设备&#xff0c;其正常运行对于企业的数据安全和业务连续性至关重要。机房内部设备众多&#xff0c;且运行过程中涉及大量的数据交换和传输。一旦发生安全事…

java 类加载器及双亲委派机制

1、 有哪些类加载器 还有自定义类加载器。最上面的为父加载器&#xff0c;加载类的路径是不一样的 2、 什么是双亲委派机制&#xff1a; 1. 加载时&#xff0c;先去找父类&#xff0c;父类无法加载时&#xff0c;在由儿子加载 3、 为什么用双亲委派&#xff1a; 沙箱安全&…

【devops】 Bytebase 一站式开源 数据库DevOps平台

初识 Bytebase 1、安装 安装地址 https://www.bytebase.com/docs/get-started/self-host/#docker 安装指令 docker run --init \--name bytebase \--publish 8080:8080 --pull always \--volume ~/.bytebase/data:/var/opt/bytebase \bytebase/bytebase:2.18.02、登录-dashboa…

OlSoul系统调校程序v2024.06.05

软件介绍 OlSoul是一款能够适配用于Win各个系统的系统调校软件&#xff0c;OlSoul内置有众多调校功能可以直接使用&#xff0c;如有启用无线网络功能、启用打印机功能、系统快速休眠与休眠开关、快捷方式小箭头去除功能等&#xff0c;具体的调校功能多达几十项&#xff0c;可自…

Android SplashActivity runs twice at launch on Android 13 API 33

运行写好的 Android App 到自己手机上&#xff08;显示Xiaomi 23013RK75C Android 13 API 33&#xff09;&#xff0c; 通过 Logcat 发现第一次安装运行时启动页面的 onCreate 被调用两次&#xff0c;第一次 onCreate 函数中的 savedInstanceState 值为空&#xff0c;第二次不…

【Python】selenium使用find_element时解决【StaleElementReferenceException】问题的方法

StaleElementReferenceException 是 Selenium WebDriver 中的一种异常&#xff0c;通常在元素与当前页面的状态不同步时抛出&#xff0c;比如页面已经刷新或导航到另一个页面&#xff0c;但是尝试操作的元素引用仍然是旧页面上的元素。 以下是一些解决 StaleElementReferenceE…

RT-DETR 详解之 Efficient Hybrid Encoder

在先前的博文中&#xff0c;博主介绍了RT-DETR在官方代码与YOLOv8集成程序中的训练与推理过程&#xff0c;接下来&#xff0c;博主将通过代码调试的方式来梳理RT-DETR的整个过程。 整体结构 RT-DETR的代码调试大家可以参考博主这篇文章&#xff1a; 在梳理整个代码之前&…

几何关系运算处理

1. 判断点在线的左边还是右边 要判断一个坐标点在直线的左侧还是右侧&#xff0c;可以使用向量叉积。具体来说&#xff0c;对于给定的直线和点&#xff0c;我们可以计算点到直线的向量与直线的方向向量的叉积。叉积的符号可以用于判断点的位置关系&#xff1a; 如果叉积为正&…

SpringBoot高手之路-原理篇

文章目录 JDK动态代理 JDK动态代理

【docker】仓库、镜像、容器的关系

Docker 是一个开源的容器化平台&#xff0c;它允许开发者将应用程序及其依赖项打包到一个可移植的容器中&#xff0c;并发布到任何流行的 Linux 机器上。在 Docker 的生态系统中&#xff0c;有几个核心概念&#xff1a;仓库&#xff08;Repository&#xff09;、镜像&#xff0…

长文预警:自动驾驶の核燃料库!Tesla数据标注系统解析

长文预警&#xff1a;自动驾驶の核燃料库&#xff01;Tesla数据标注系统解析 前言 本文整理自原文链接&#xff0c;写的非常好&#xff0c;给了博主很多启发&#xff0c;投原创是因为平台机制&#xff0c;希望能被更多人看到。 掐指一算&#xff0c;又到了该学习的时间&#…

Python | Leetcode Python题解之第137题只出现一次的数字II

题目&#xff1a; 题解&#xff1a; class Solution:def singleNumber(self, nums: List[int]) -> int:a b 0for num in nums:b ~a & (b ^ num)a ~b & (a ^ num)return b

JSON及Python操作JSON相关

JSON及Python操作JSON相关 Json简介及Python操作Json相关示例。 1. JSON概念及支持的数据类型 1.1 什么是 JSON&#xff1f; JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解…

56.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;55.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露&#xff08;3&#xff09; 如果把文…

【PyTorch】PyTorch深度学习框架实战(二):torchrun

一、引言 PyTorch由facebook人工智能研究院研发&#xff0c;2017年1月被提出&#xff0c;是一个开源的Python机器学习库&#xff0c;基于Torch&#xff0c;用于自然语言处理等应用程序。PyTorch既可以看作加入了GPU支持的numpy&#xff0c;同时也可以看成一个拥有自动求导功能的…

SpringAI调用OpenAI Demo

Spring AI 在maven的setting.xml <mirror> <id>spring-milestones</id> <name>Spring Milestones</name> <mirrorOf>spring-milestones</mirrorOf> <url>https://repo.sprin…

AI学习指南机器学习篇-决策树的特征选择和分裂准则

AI学习指南机器学习篇-决策树的特征选择和分裂准则 1. 特征选择的方法 在机器学习中&#xff0c;特征选择是一项非常重要的任务&#xff0c;它直接影响到模型的性能和泛化能力。决策树是一种常用的机器学习算法之一&#xff0c;而特征选择则是决策树构建过程中的关键环节。常…