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 main

import (
	"fmt"
	"time"
)

func main() {
	chan1 := make(chan int)
	chan2 := make(chan int)

	go func() {
		chan1 <- 1
		time.Sleep(5 * time.Second)
	}()
	go func() {
		chan2 <- 1
		time.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 main

import (
	"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 main

func main() {
  select {
  }
}

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

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

package main

import (
	"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 <- true
	fmt.Println("定时任务结束")
}

结果:

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

超时退出实现代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	timeout := 5 * time.Second
	done := 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 main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	timeout := 5 * time.Second
	ctx, 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         // chan
	elem 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. 否则循环此过程

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【js】input设置focus()不生效

实现功能&#xff1a;点击添加文章标签的时候&#xff0c;输入框聚焦。 页面上&#xff0c;input输入框默认不显示&#xff0c;是display:none; 点击添加按钮后&#xff0c;input输入框才显示。 在js里面直接获取元素进行设置聚焦不成功 。 ∵ focus方法比show方法先执行。j…

自考搜题网?5个大学生必备的搜题 #其他#其他#媒体

在大学生的学习过程中&#xff0c;遇到难题和疑惑是常有的事情。然而&#xff0c;随着互联网的普及和技术的发展&#xff0c;搜题和学习软件成为了大学生们解决问题的利器。今天&#xff0c;我将向大家推荐几款备受大学生喜爱的搜题和学习软件&#xff0c;帮助我们更好地应对学…

系统架构设计师【第19章】: 大数据架构设计理论与实践 (核心总结)

文章目录 19.1 传统数据处理系统存在的问题19.2 大数据处理系统架构分析19.2.1 大数据处理系统面临挑战19.2.2 大数据处理系统架构特征 19.3 Lambda架构19.3.1 Lambda架构对大数据处理系统的理解19.3.2 Lambda架构应用场景19.3.3 Lambda架构介绍19.3.4  Lambda架构的实…

推荐一款AI音乐生成工具和一款浏览器

大家好&#xff0c;今天给大家带来2款软件&#xff0c;一款是移动浏览器&#xff0c;一款是AI音乐生成软件。 Alook Alook是一款移动端浏览器&#xff0c;它以其独特的无广告、无推送、无新闻的"三无"特性&#xff0c;为用户提供了一个清爽的上网环境。Alook不仅界…

惠海 H5528 升降压芯片 12V24V36V48V60V75V LED恒流驱动IC 调光细腻顺滑无阶梯感

惠海H5528是一款升压、降压、升压降压的LED恒流驱动IC&#xff0c;其具备宽范围调光比且无频闪调光的特性&#xff0c;使得它在智能照明、Dali调光、0~10V调光、摄影灯照明以及补光灯照明等多种应用中具有广泛的应用前景。 这款芯片支持降压、升压和升降压拓扑的应用&#xff0…

elementui Menu 二级菜单 min-width修改无效

原因&#xff1a;可能是生成的二级菜单样式里面没有带特定的hash属性 而vue代码里面样式里带了 scoped生成的样式有改样式选择器 从而无法成功选择 解决&#xff1a;让样式可以全局选择 不带属性选择器 单文件组件 CSS 功能 | Vue.js :global(.el-menu--vertical .el-menu--p…

CCF-GESP 等级考试 2023年9月认证C++四级真题解析

一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 第 1 题 ⼈们所使⽤的⼿机上安装的App通常指的是&#xff08; &#xff09;。 A. ⼀款操作系统B. ⼀款应⽤软件C. ⼀种通话设备D. 以上都不对 正确答案&#xff1a;B. ⼀款应⽤软件 解析&#xff1a;App是"…

CSS实现3个圆点加载动画

加载动画主要使用了css的animation和transform属性&#xff0c;animation用来实现动画效果&#xff0c;transform实现过渡&#xff0c;让动画看起来更真实 一、html <div class"loadding-box"><div class"dot1"></div><div class&qu…

神经网络 torch.nn---Convolution Layers

torch.nn — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) torch.nn和torch.nn.functional的区别 torch.nn是对torch.nn.functional的一个封装&#xff0c;让使用torch.nn.functional里面的包的时候更加方便 torch.nn包含了torch.nn.…

Promed Bioscience—高纯度胶原蛋白

Promed Bioscience——高纯度胶原蛋白供应商 专于研发&#xff0c;忠于质量&#xff0c;创新驱动 AXXORA 作为Enzo life sciences公司的子公司&#xff0c;是欧美最大的生命科学研究信息、服务、销售电子一站式服务平台之一&#xff0c;AXXORA精选欧洲四十多家优秀的生命科学研…

2.Rust自动生成文件解析

目录 一、生成目录解析二、生成文件解析2.1 Cargo.toml2.2 main函数解析 一、生成目录解析 先使用cargo clean命令删除所有生成的文件&#xff0c;下图显示了目录结构和 main.rs文件 使用cargo new testrust时自动创建出名为testrust的Rust项目。内部主要包含一个src的源码文…

问题:歌剧序曲是用什么曲式写成? #学习方法#其他#经验分享

问题&#xff1a;歌剧序曲是用什么曲式写成&#xff1f; A、贝多芬 B、海顿 C、肖邦 D、莫扎特 参考答案如图所示

AI重塑搜索和浏览器,360打造AIPC轻量化方案

6月6日&#xff0c;360AI新品发布会暨开发者沟通会在京举办&#xff0c;360集团发布全新360AI搜索、360AI浏览器&#xff0c;360集团创始人周鸿祎在现场使用360AI搜索为2024年高考语文作文押题。同时&#xff0c;“360AI甄选”平台及会员体系“360AI大会员”正式上线&#xff0…

S3Dlib | 太炫酷!所有3D图形它都可以绘制...

前言 一、「s3dlib」-Python中王炸3D绘图神器 二、可视化学习圈子是干什么的&#xff1f; 三、系统学习可视化 四、猜你喜欢 前言 我们的数据可视化课程已经上线啦&#xff01;&#xff01;目前课程的主要方向是 科研、统计、地理相关的学术性图形绘制方法&#xff0c;后续…

【论文速读】| BIOCODER:一个具有上下文实用知识的生物信息学代码生成基准测试

本次分享论文&#xff1a;BIOCODER: A Benchmark for Bioinformatics Code Generation with Contextual Pragmatic Knowledge 基本信息 原文作者&#xff1a;Xiangru Tang, Bill Qian, Rick Gao, Jiakang Chen, Xinyun Chen, Mark Gerstein 作者单位&#xff1a;耶鲁大学, Go…

【SpringBoot + Vue 尚庭公寓实战】租期管理接口实现(四)

【SpringBoot Vue 尚庭公寓实战】租期管理接口实现&#xff08;四&#xff09; 文章目录 【SpringBoot Vue 尚庭公寓实战】租期管理接口实现&#xff08;四&#xff09;1、查询全部租期列表2、保存或更新租期信息3、根据ID删除租期 租期管理共有三个接口&#xff0c;分别是 保…

HDFS的块汇报和块放置策略--从一次HDFS写文件故障开始(WIP)

文章目录 前言事故分析&#xff1a;代码解析BlockReport的调度和时机哪些Block会进行FBR或者IBRIBR所选定的BlockFBR所选定的Block 块放置策略详解之在上层寻找机器为文件添加块的基本流程块放置策略&#xff1a;选择机器为每个副本逐个寻找机器在指定范围内随机寻找简单看一下…

如何学习使用淘宝API?淘宝API运营场景

学习使用淘宝API涉及对其功能、分类、调用方法及实际应用的综合理解。下面按部分详细解释如何系统地学习和掌握淘宝API的使用&#xff1a; 淘宝API接口入门 了解淘宝开放平台&#xff1a;淘宝开放平台为开发者提供了一个可以与淘宝数据进行交互的平台&#xff0c;涵盖了丰富的A…

在当前页面拿到抽屉弹窗页面中从后端返回的值 #Vue3 #两个.vue页面之间传值问题

在当前页面拿到抽屉弹窗页面中从后端返回的值 #Vue3 #两个.vue页面之间传值问题 *解决方法一&#xff1a; 将抽屉弹窗里从后端返回得到的值缓存在浏览器中&#xff0c;在当前页面中从浏览器中获取该值。 &#xff08;原理其实就是借助第三个盒子来传递一下值&#xff0c;太小学…

C#操作MySQL从入门到精通(12)——对查询结果进行计算

前言 我们有时候需要对查询到的数据进行一些计算,比如将两个列的数据拼接在一起,比如将两个列的数据进行相乘,Mysql中是支持这种操作的,下面我来详细介绍: 本篇文章测试使用的数据库如下: 1、将两个列的数据进行拼接 我们将姓名和年龄这两个列的数据进行拼接,如下:…