【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.hqwc.cn/news/325134.html

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

相关文章

春招冲刺第一天:Excel入门

春招冲刺第一天 前言&#xff1a; 转行换方向了家人们&#xff0c;准备往数据分析那转了&#xff0c;实习我现在也找不到&#xff0c;打算先猛学两周技术&#xff0c;过完年再投简历了。 时间确实非常紧张&#xff0c;目前一天计划学8小时以上&#xff0c;主要参考视频——&g…

【JVM】类加载器ClassLoader

一、简介 在Java中&#xff0c;类加载器&#xff08;ClassLoader&#xff09;是一个关键的组件&#xff0c;它负责将字节码文件加载到内存并转换成Java类。Java的类加载器主要可以分成两类&#xff1a;系统提供的和由Java应用开发人员编写的。Java开发者可以根据需要创建自己的…

11.2 Linux串口驱动框架

tty 驱动程序框架 tty 驱动程序从下往上分别是设备驱动层、行规程、终端虚拟化、TTY I/O层&#xff0c;它们的功能如下&#xff1a; 设备驱动层&#xff1a;用于驱动设备&#xff0c;如串口、显示器、键盘等。行规程&#xff1a;用于处理控制字符、回显输入数据、缓存输入数据…

vue3-admin-element框架实现动态路由(根据接口返回)

第一步&#xff1a;在src-utils-handleRoutes&#xff0c;修改代码&#xff1a; export function convertRouter(routers) {let array routersrouters []for (let i in array) {for(let s in asyncRoutes){if (array[i].path asyncRoutes[s].path) {routers.push(asyncRout…

java 音乐会售票平台系统Myeclipse开发mysql数据库struts2结构java编程计算机网页项目

一、源码特点 java 音乐会售票平台系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助struts2框架开发mvc模式&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发 环境为TOCAT7.0,Myeclipse8.5开发&#xff0c;数据…

python练习3【题解///考点列出///错题改正】

一、单选题 1.【单选题】 ——可迭代对象 下列哪个选项是可迭代对象&#xff08; D&#xff09;&#xff1f; A.(1,2,3,4,5) B.[2,3,4,5,6] C.{a:3,b:5} D.以上全部 知识点补充——【可迭代对象】 可迭代对象&#xff08;iterable&#xff09;是指可以通过迭代&#xff…

C#,归并排序算法(Merge Sort Algorithm)的源代码及数据可视化

归并排序 归并算法采用非常经典的分治策略&#xff0c;每次把序列分成n/2的长度&#xff0c;将问题分解成小问题&#xff0c;由复杂变简单。 因为使用了递归算法&#xff0c;不能用于大数据的排序。 核心代码&#xff1a; using System; using System.Text; using System.Co…

Node.js基础知识点(二)

一、Node环境安装&#xff08;Windows&#xff09; 1.下载对应的node.js版本:https://nodejs.org/en/download/ 2.下载完成后&#xff0c;双击安装包&#xff0c;开始安装node.js 3.勾选复选框&#xff0c;点击【Next】按钮 4.修改好目录后&#xff0c;点击【Next】按钮 5.此处…

得物商品状态体系介绍

一、得物的商品体系 目前得物的商品分为三种类型&#xff0c;分别是&#xff1a;新品、商品、草稿。但是只有商品是可售卖的&#xff0c;新品和草稿都不是可售卖的。 新品有很多种创建的渠道&#xff0c;商品可以由新品选品通过后由系统自动生成&#xff0c;也可以由运营直接…

WPF 入门教程DispatcherTimer计时器

https://www.zhihu.com/tardis/bd/art/430630047?source_id1001 在 WinForms 中&#xff0c;有一个名为 Timer 的控件&#xff0c;它可以在给定的时间间隔内重复执行一个操作。WPF 也有这种可能性&#xff0c;但我们有DispatcherTimer控件&#xff0c;而不是不可见的控件。它几…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)创建一个TcpConnection实例 以及 接收客户端数据

#CSDN 年度征文&#xff5c;回顾 2023&#xff0c;赢专属铭牌等定制奖品# 一、主线程反应堆模型的事件添加和处理详解 >>服务器和客户端建立连接和通信流程&#xff1a; 基于多反应堆模型的服务器结构图&#xff0c;这主要是一个TcpServer&#xff0c;关于HttpServer,…

图像分割实战-系列教程11:U2NET显著性检测实战3

&#x1f341;&#x1f341;&#x1f341;图像分割实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 U2NET显著性检测实战1 U2NET显著性检测实战2 U2NET显著性检测实战3 6、上采样操作与REBNCONV def…