【go语言】select多路选择

select基础知识

select 是 Go 语言中用于处理通道操作的控制结构,它类似于 switch 语句,但专门用于通道的选择。select 语句使得一个 goroutine 可以等待多个通道操作,当其中任意一个通道操作可以进行时,就会执行相应的 case 分支。

select语句语法如下:

select {
case channel1 <- value1:// 如果 channel1 可以写入,执行这里
case value2 := <-channel2:// 如果 channel2 可以读取,执行这里
case value3, ok := <-channel3:// 如果 channel3 被关闭,并且有数据可读,执行这里
case <-time.After(time.Second):// 在超时时间内没有任何 case 可执行,执行这里
default:// 如果没有任何 case 可执行,执行这里
}

特性: 

  1. 如果多个 case 同时满足条件,Go 会随机选择一个执行。
  2. 如果没有 case 可以执行,且存在 default 分支,则执行 default
  3. 如果没有 default 分支,select 会阻塞,直到至少有一个 case 可执行。
  4. select 可以和 for 循环一起使用,用于不断地处理通道操作。

select 的主要用途是处理并发编程中的多个通道操作,例如处理超时、非阻塞通信等场景。

1. 超时控制

超时会比程序请求失败还可怕,为了避免主线程阻塞可以在select中设置超时中断。

示例代码如下:通过多路选择等待任务完成,如果超时就直接执行其他的处理程序

package mainimport ("fmt""time"
)// 超时控制
func main() {select {case re := <-AsynService():fmt.Print("任务完成", re)case <-time.After(time.Millisecond * 3000):fmt.Print("超时啦")//default://	fmt.Print("不能阻塞")}}// 服务
func service() string {time.Sleep(time.Millisecond * 3000)return "finish"
}// 异步启动服务
func AsynService() chan string {rechan := make(chan string, 1)go func() {res := service()time.Sleep(time.Millisecond * 1000)rechan <- res}()return rechan
}

2. 任务取消

(1)获取取消通知

// 3.select判断任务是否取消
func isCanceled(cn chan struct{}) bool {select {case <-cn:fmt.Println("任务取消")return truedefault:fmt.Println("任务不取消,继续执行")return false}
}

(2)发送取消消息

// 1.普通向cancel通道发送取消通知,这种做法需要事先知道有多少个正在执行的任务
func cancel1(cn chan struct{}) {cn <- struct{}{}
}// 2.向采取close方法关闭所有任务
func cancel2(cn chan struct{}) {close(cn)
}

(3)测试

// 测试任务取消
func TestCancel(t *testing.T) {cn := make(chan struct{})for i := 1; i < 6; i++ {go func(cn chan struct{}) {for {if isCanceled(cn) {break} else {time.Sleep(time.Millisecond * 1000)}}fmt.Println("任务取消")}(cn)}cancel2(cn)}

六个go程进行监听任务是否取消,普通发送取消通知只会取消一个go程,关闭通道可以取消所有在监听的go程。

3. Context任务取消

(1)Context介绍

在 Go 语言中,context.Context 是一个标准库中非常常用的接口,它提供了在多个 goroutine 之间传递请求范围的截止日期、取消信号、存储值等信息的途径。context.Context 主要用于在函数之间传递请求的截止日期、取消信号、跟踪信息以及其他请求范围的值。

type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}
}
  • Deadline():返回 Context 的截止日期(即取消的时间点)和一个布尔值,表示是否设置了截止日期。
  • Done():返回一个 <-chan struct{} 类型的通道,该通道关闭时表示 Context 被取消或者达到了截止日期。
  • Err():返回一个错误,表示 Context 被取消的原因。
  • Value(key interface{}):根据给定的键返回相关联的值,通常用于传递请求范围的值。

context 包还提供了一些函数用于创建和操作 Context

  • context.Background():返回一个空的 Context,常用于表示整个请求生命周期。
  • context.TODO()TODO 表示 "to do",返回一个空的、不可取消的 Context
  • context.WithCancel(parent Context) (ctx Context, cancel CancelFunc):返回一个可取消的 Context 和一个对应的 CancelFunc,可以用来取消该 Context
  • context.WithDeadline(parent Context, d time.Time) (Context, CancelFunc):返回一个带有截止日期的 Context
  • context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc):返回一个带有超时时间的 Context
  • context.WithValue(parent Context, key, val interface{}) Context:返回一个包含指定键值对的 Context

(2)任务取消

发送取消消息:

func isCanceled2(ctx context.Context) bool {select {case <-ctx.Done():return truedefault:return false}
}

我们可以看一下ctx.Done()的源码:

Done()方法返回一个channel,直接读取这个channel将会被阻塞,让我们看一下cancelCtx源码:

由上面的结构体可以知道这里c.done是一个原子操作的值,采用懒加载方法,被第一次调用的cancel方法关闭。
调用Done函数时已经存在一个取消通道时就直接返回,当是第一个调用的就创建channel并返回,都是并发安全的。

测试:

func TestContextCancel(t *testing.T) {ctx, cancel := context.WithCancel(context.Background())for i := 1; i < 6; i++ {go func(i int, ctx context.Context) {for {if isCanceled2(ctx) {break} else {time.Sleep(time.Millisecond * 100)}}fmt.Println(i, "Cancelled")}(i, ctx)}cancel()time.Sleep(time.Second * 1)
}

这里通过context.Background()函数获得顶级context,通过WithCancel函数获取一个子上下文和一个取消函数,通过取消顶级context可以取消所有子上下文达到任务取消目的,或者是子上下文其自身取消。

 让我们看一下cancel方法:cancel方法关闭c.done也就是关闭了这个chan通道通知任务取消,同时也递归取消所有的子上下文,如果removeFromParent参数为true将会从父context移除掉当前子context。

// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
// cancel sets c.cause to cause if this is the first time c is canceled.
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {if err == nil {panic("context: internal error: missing cancel error")}if cause == nil {cause = err}c.mu.Lock()if c.err != nil {c.mu.Unlock()return // already canceled}c.err = errc.cause = caused, _ := c.done.Load().(chan struct{})if d == nil {c.done.Store(closedchan)} else {close(d)}for child := range c.children {// NOTE: acquiring the child's lock while holding parent's lock.child.cancel(false, err, cause)}c.children = nilc.mu.Unlock()if removeFromParent {removeChild(c.Context, c)}
}

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

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

相关文章

Reids原理及简单命令

目录 1.关系数据库与非关系型数据库 关系型数据库 非关系型数据库 关系型数据库和非关系型数据库区别 数据存储方式不同 扩展方式不同 对事务性的支持不同 总结&#xff1a; 2. Redis简介 什么是reids reids优点 reids使用场景&#xff1a; reids快的原因 Redis数…

阿里面试:redis 为什么把简单的字符串设计成 SDS?

面试官:了解redis的String数据结构底层实现嘛? 铁子:当然知道,是基于SDS实现的 面试官:redis是用C语言开发的,那为啥不直接用C的字符串,还单独设计SDS这样的结构呢? 铁子: 其实看得出面试官是想看看,铁子是只停留在redis的使用层面,还是对底层数据结构有过更深入的…

java每日一题——抽红包(答案及编程思路)

前言&#xff1a; 打好基础&#xff0c;daydayup! 题目要求&#xff1a;假设某主播时发起了抢红包活动&#xff0c;五个红包金额分别为9&#xff0c;666&#xff0c;188&#xff0c;520&#xff0c;99999。粉丝按照先来先得&#xff0c;随机抽取&#xff0c;抽完为止&#xff0…

python如何循环读取excel一列中两个单元格之间的数据并写入文本文件?

python如何循环读取excel一列中两个单元格之间的数据并写入文本文件&#xff1f; ━━━━━━━━━━━━━━━━━━━━━━ python如何循环读取excel一列中两个单元格之间的数据&#xff0c;如B2到B22&#xff0c;并写入文本文件&#xff1f; 你可以使用 Python 中的 op…

功能强大且直观的日程和任务管理工具—Things 3 for Mac

在现代生活中&#xff0c;我们面对着繁忙的日程安排和众多的任务&#xff0c;我们需要一款高效的工具来帮助我们管理和组织这些事务。而事务管理的首选工具&#xff0c;非 Things 3 for Mac 莫属。 Things 3 for Mac 是一款功能强大且直观的日程和任务管理工具。它的设计简洁&…

Vue3——element-plus表格组件怎样得到当前行的id

实现方法&#xff1a; <el-table-column property"address" label"操作" show-overflow-tooltip header-align"center" v-slot"scope"><el-button type"success" click"editBtn(scope.row.id)">编辑…

01-线程池项目背景:C++的数据库操作

从0开始学习C与数据库的联动 1.原始方式-使用MySQL Connector/C 提供的API查询 1.1 数据库预操作 我的本地电脑上有mysql数据库&#xff0c;里面预先创建了一个database名叫chat&#xff0c;用户名root&#xff0c;密码password。 1.2 Visual Studio预操作 在Windows上使用…

【unity】Obi插件架构组成(参数详细解释)——解算器四面板设置、三种更新器、参与者介绍

文章目录 一、架构&#xff08;Architecture&#xff09;1.1 Obi解算器&#xff08;ObiSolver&#xff09;1.2 ObiUpdater1.3 ObiActorBlueprint1.4 Obi参与者&#xff08;ObiActor&#xff0c;如ObiRope等&#xff09; 二、Obi解算器&#xff08;ObiSolver&#xff09;2.1 解算…

win11家庭版开启远程桌面功能

win11家庭版不支持远程桌面 下载补丁 https://download.csdn.net/download/yonggeit/88706714 用谷歌浏览器会提示危险文件&#xff0c;选择“保留危险文件”即可&#xff0c;如果大家不放心&#xff0c;可用杀软进行查杀。 解压后 选择“install.bat”右键选择“以管理员身份…

【C语言刷题每日一题#牛客网BC107】矩阵转置

目录 问题描述 思路逐步分析 完整代码实现 结果测试 问题描述 思路逐步分析 首先&#xff0c;根据输入的描述&#xff0c;第一行输入的是两个整数n和m&#xff0c;分别表示一个矩阵&#xff08;二维数组&#xff09;的行和列&#xff0c;并且行和列不超过10 根据要求&…

鸡目标检测数据集VOC格式500张

鸡&#xff0c;一种家禽&#xff0c;是人类的重要食物来源之一&#xff0c;也是农业生产中的重要组成部分。 鸡的外观相对较为简单&#xff0c;身体呈圆锥形&#xff0c;羽毛密集&#xff0c;双翅短小&#xff0c;无法飞行。鸡的头部较小&#xff0c;嘴巴尖锐&#xff0c;方便…

电商要怎么学?企业如何进行数字化转型打破市场僵局?

电商要怎么学&#xff1f;企业如何进行数字化转型打破市场僵局&#xff1f; 电商的学习需要从多个方面入手&#xff0c;首先需要了解电商的基本概念和原理&#xff0c;包括电商平台的运营模式、商品推广、客户服务等。此外&#xff0c;还需要掌握电商平台的操作技能&#xff0c…

Pygame中监控键盘按键的方法

1 事件与队列 在Pygame中&#xff0c;将用户对游戏的操作叫做“事件”。键盘按键是一种事件&#xff0c;鼠标点击和游戏手柄的输入也是一种事件。在Pygame的子模块locals中&#xff0c;对这些事件进行了定义。当用户通过键盘、鼠标或者游戏手柄对游戏进行操作后&#xff0c;产…

SpringBoot项目部署(Docker)——通过Dockerfile将打包好的jar包创建成镜像 在IDEA中配置docker,一键启动容器 用swagger进行测试

目录 引出SpringBoot项目部署&#xff08;jar包&#xff09;Dockefile初识idea配置docker在Linux中配置docker remote api配置idea docker插件 部署项目Springboot整合Dockerfile准备项目创建Dockerfile文件进行测试 总结 引出 1.Dockerfile命令初识&#xff0c;CMD…; 2.idea配…

Fiddler抓取HTTPS最全(强)攻略

对于想抓取HTTPS的测试初学者来说&#xff0c;常用的工具就是fiddler。可是在初学时&#xff0c;大家对于fiddler如何抓取HTTPS真是伤了脑筋&#xff0c;可能你一步步按着网上的帖子成功了&#xff0c;那当然是极好的。 有可能没有成功&#xff0c;这时候你就很抓狂了&#xff…

Unity3D Shader 之透视效果XRay

1、 Shader "Unlit/XRay" {Properties{_MainTex("Texture", 2D) "white" {}// 漫反射_Diffuse("Diffuse", COLOR) (1,1,1,1)// XRay 效果_XRayColor("XRay Color", COLOR) (0,1,1,1)_XRayPower("XRay Power",…

RxJS 操作符-学习笔记

提前准备&#xff1a; pipe 方法: 用于组合多个操作符,可以将一系列操作符作为参数传递给 pipe 方法&#xff0c;这些操作符将 依次 对数据流进行处理。这里的依次很关键&#xff0c;也代表着pipe()中组合的这么几个操作符的执行顺序就是从开始一直到结束的&#xff0c;其中的…

Mysql 将表里的两列值数据互换

示例&#xff1a; 需要将表中的 两个订单号互换 方案&#xff1a; 将同一张表数据做 临时数据 和主表 做数据交互 。 update 表 as main, 表 as temp set main.bill_no temp.track_bill_no, main.track_bill_no temp.bill_no where main.id temp.id…

小白的2023总结与未来的妄想

嘿&#xff0c;各位&#xff0c;很高兴各位能看到这篇文章&#xff0c;先祝各位家人2024财源茂盛达三江&#xff0c;生意兴隆通四海&#xff01;&#xff01;&#xff01;&#xff01; 那么作为一个还在读大三的入门小白&#xff0c;这一年得益于身边的各个校外大佬与校内老师的…

【Oracle数据库】两个服务端通过一个端口分别读写两个数据库账户下的表

允许两个服务端文件通过一个端口分别读写两个数据库账户下的表格数据&#xff0c;并且这两个用户名下的表格数据在一个表空间&#xff01; Q&#xff1a;一个Oracle表空间中两个用户下面的表&#xff0c;我用服务端写入和读取这两个用户下面的表格时&#xff0c;可以用一样的端…