当前位置: 首页 > news >正文

[Golang] Context

[Golang] Context

文章目录

  • [Golang] Context
    • 什么是context
    • 创建context
      • 创建根context
      • 创建context
    • context的作用
      • 并发控制
      • context.WithCancel
      • context.WithDeadline
      • context.WithTimeout
      • context.WithValue

什么是context

Golang在1.7版本中引入了一个标准库的接口context,定义:

type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key any) any
}

它定义了四个方法:

  • Deadline:设置context.Context被取消的时间,即截止日期

  • Done:返回一个只读channel,当Context到达截止日期时或被取消,这个channel就会被关闭,表示Context的链路结束,多次调用Done会返回同一个channel

  • Err:返回Context结束的原因,它只会在Done返回的channel被关闭时,才会返回非空的值;

    • 情况1:Context被取消:返回Canceled
    • 情况2:Context超时:返回DeadlineExceeded
  • Value:从context.Context中获取键对应的值,类似与map的get方法,对于同一个Context,多次调用Value并传入相同的key会返回相同的结果,如果没有对应的key就返回nil。

    • 键值对通过WithValue方法写入
func WithValue(parent Context, key, val any) Context {if parent == nil {panic("cannot create context from nil parent")}if key == nil {panic("nil key")}if !reflectlite.TypeOf(key).Comparable() {panic("key is not comparable")}return &valueCtx{parent, key, val}
}

创建context

创建根context

两种方法:

  • context.Background()
  • context.TODO()

两者没有什么太多的区别,都是创建根context,根context是一个空的context,不具备任何功能。

一般情况下,当前函数没有上下文作为入参,我们就使用context.Background()创建一个根context作为起始的上下文向下传递。

创建context

根context被创建后,不具备任何功能,为了让context在程序中发挥作用,我们需要依靠包提供的With系列函数来进行派生。

四个派生函数:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {...}
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {...}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {...}
func WithValue(parent Context, key, val any) Context {...}

基于当前context,每个With函数都会创建出一个新的context,这类似于我们熟悉的树结构,当前context称为父context,派生出的每个新context被称为子context。

image-20240918180659297

通过根context的四个With函数派生出四种类型的context,每种context又可以通过同样的方式调用with系列方法继续向下派生出新的context,整体结构像一个树一样。

context的作用

  • 用于并发控制,控制协程的退出
  • 上下文信息的传递

总的来说,就是用来在父子goroutine间进行值传递和发生cancel信号的一种机制。

并发控制

一般的服务器都是一直运行的,等待客户端或者浏览器的请求做出响应,思考这种场景,一个微服务架构中下,服务器收到一个请求后,并不会在一个goroutine下完成(如果逻辑复杂),而是创建很多goroutine共同完成这个请求。

假设有rpc1—rpc2—rpc3—rpc4—rpc5,5个rpc调用。

但是如果在整个rpc调用中,如果rpc1就出现了错误,如果没有context存在,服务器就会坚持调用完整个流程,也就是等待所有rpc调用完成后才能返回结果,但是实际上这样浪费了不少的时间,单纯浪费计算和IO资源(rpc1错误之后的rpc调用都是无用功)。因为rpc调用之间不知道已经产生了错误,而context就很好的解决了这个问题。

在不需要子goroutine继续执行的时候,通过context通知子goroutine关闭即可。

context.WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {c := withCancel(parent)return c, func() { c.cancel(true, Canceled, nil) }
}

context.WithCancel函数是一个取消控制函数,只需要一个context作为参数,能够衍生出一个新的子context和取消函数Cancel,我们可以通过这个将这个子context传入子goroutine中,执行Cancel函数来关闭这个子goroutine,当前的上下文和它的子上下文都会被取消,所有的goroutine都会同步收到取消信号。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx, cancel := context.WithCancel(context.Background())go watch(ctx, "goroutine1")go watch(ctx, "goroutine2")time.Sleep(3 * time.Second)fmt.Println("end!!!")cancel()time.Sleep(time.Second)
}
func watch(ctx context.Context, name string) {for {select {case <-ctx.Done():fmt.Println(name, " exit")returndefault:fmt.Println(name, " watching")time.Sleep(time.Second)}}
}

执行结果:

image-20240918184141136

通过WithCancel函数派生出一个带有返回函数cancel的ctx:ctx, cancel := context.WithCancel(context.Background()),并且把ctx传入子goroutine中,在3秒内没有执行cancel,子goroutine将一直执行default语句,3秒后,执行cancel,此时子goroutine从ctx.Done()收到消息,执行return结束。

context.WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {return WithDeadlineCause(parent, d, nil)
}

context.WithDeadline函数也是一个取消控制函数,共有两个参数,一个是context,另一个是截止时间,同样会返回一个子context和取消函数cancel。在使用时,如果没有到截止日期,我们可以通过调用cancel函数来手动取消context,控制goroutine的退出,如果到了截止日期,我们都没有调用cancel函数,子context的Done()管道也会收到一个取消信号,来控制子goroutine的退出。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second))defer cancel()go watch(ctx, "goroutine1")go watch(ctx, "goroutine2")// 让goroutine1和goroutine2先执行5秒time.Sleep(5 * time.Second)fmt.Println("end!!!")}
func watch(ctx context.Context, name string) {for {select {case <-ctx.Done()://但是不到5秒,3秒时收到了退出信号fmt.Println(name, " exit")returndefault:fmt.Println(name, " watching")time.Sleep(time.Second)}}
}

执行结果:

image-20240918210655210

我们并没有调用cancel函数,但是在过了3秒后,子goroutine里ctx.Done()收到了信号,子goroutine进行退出。

context.WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {return WithDeadline(parent, time.Now().Add(timeout))
}

context.WithTimeoutcontext.WithDeadline差不多,都是用于超时取消子context,只是第二个参数有点区别,不是具体时间,而是时间长度。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()go watch(ctx, "goroutine1")go watch(ctx, "goroutine2")// 让goroutine1和goroutine2先执行5秒time.Sleep(5 * time.Second)fmt.Println("end!!!")}
func watch(ctx context.Context, name string) {for {select {case <-ctx.Done()://但是不到5秒,3秒时收到了退出信号fmt.Println(name, " exit")returndefault:fmt.Println(name, " watching")time.Sleep(time.Second)}}
}

执行结果:

image-20240918211051169

执行结果和context.WithDeadline类似。

context.WithValue

func WithValue(parent Context, key, val any) Context {if parent == nil {panic("cannot create context from nil parent")}if key == nil {panic("nil key")}if !reflectlite.TypeOf(key).Comparable() {panic("key is not comparable")}return &valueCtx{parent, key, val}
}

context.WithValue函数从父context中创建一个子context用于传值,函数参数是父context、key、val,返回一个context。一般用于上下文信息的传递,比如请求唯一id,以及trace_id,用于链路追踪以及配置穿透。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx := context.WithValue(context.Background(), "name", "张三")go func1(ctx)time.Sleep(time.Second)
}
func func1(ctx context.Context) {fmt.Println("name = ", ctx.Value("name").(string))
}

执行结果:

image-20240918211613966

相关文章:

[Golang] Context

[Golang] Context 文章目录 [Golang] Context什么是context创建context创建根context创建context context的作用并发控制context.WithCancelcontext.WithDeadlinecontext.WithTimeoutcontext.WithValue 什么是context Golang在1.7版本中引入了一个标准库的接口context&#xf…...

【JAVA集合总结-壹】

文章目录 synchronized 的实现原理以及锁优化&#xff1f;ThreadLocal原理&#xff0c;使用注意点&#xff0c;应用场景有哪些&#xff1f;synchronized和ReentrantLock的区别&#xff1f;说说CountDownLatch与CyclicBarrier 区别Fork/Join框架的理解为什么我们调用start()方法…...

Mysql梳理7——分页查询

目录 7、分页查询 7.1 背景 7.2 实现规则 分页原理 7.3 使用 LIMIT 的好处 7、分页查询 7.1 背景 背景1&#xff1a;查询返回的记录太多了&#xff0c;查看起来很不方便&#xff0c;怎么样能够实现分页查询呢&#xff1f; 背景2&#xff1a;表里有 4 条数据&#xff0c…...

智能制造与工业互联网公益联播∣企企通副总经理杨华:AI的浪潮下,未来智慧供应链迭代方向

近两年在IT圈子里面&#xff0c;AI毫无疑问是最火的一个词语&#xff0c;最近的ChatGPT、文心一言、通义千问&#xff0c;从千亿参数到万亿参数&#xff0c;再往前就是Sora文生视频异军突起... 在人工智能的浪潮下&#xff0c;AI之于供应链的价值体现在哪些地方&#xff1f;其发…...

《深度学习》—— 卷积神经网络(CNN)的简单介绍和工作原理

文章目录 一、卷积神经网络的简单介绍二、工作原理(还未写完)1.输入层2.卷积层3.池化层4.全连接层5.输出层 一、卷积神经网络的简单介绍 基本概念 定义&#xff1a;卷积神经网络是一种深度学习模型&#xff0c;通常用于图像、视频、语音等信号数据的分类和识别任务。其核心思想…...

数据结构:线性表

1、线性表概述 1.1线性表的定义 线性表&#xff08;list&#xff09;&#xff1a;零个或多个数据元素的有限序列。 简单地来说&#xff0c;我们可以用下面这张图来描述一个线性表&#xff1a; 1.2 线性表的存储结构 1.2.1顺序存储结构——顺序表 顺序表是将数据全部存储到…...

Ansible PlayBook实践案例

一、PlayBook介绍 1.什么是playbook playbook 顾名思义&#xff0c;即剧本&#xff0c;现实生活中演员按照剧本表演&#xff0c;在 ansible 中&#xff0c;由被控计算机表演,进行安装&#xff0c;部署应用&#xff0c;提供对外的服务等&#xff0c;以及组织计算机处理各种各样…...

Tomcat后台弱口令部署war包

1.环境搭建 cd /vulhub/tomcat/tomcat8 docker-compose up -d 一键启动容器 2.访问靶场 点击Manager App tomcat8的默认用户名和密码都是tomcat进行登录 3.制作war包 先写一个js的一句话木马 然后压缩成zip压缩包 最后修改后缀名为war 4.在网站后台上传war文件 上传war文件…...

胤娲科技:DeepMind的FermiNet——带你穿越“薛定谔的早餐桌”

当AI遇上量子迷雾&#xff0c;FermiNet成了你的“量子导航仪” 想象一下&#xff0c;你早晨醒来&#xff0c;发现家里的厨房变成了薛定谔的实验室&#xff0c;你的咖啡杯和吐司同时处于“存在与不存在”的叠加态。 你伸手去拿&#xff0c;却不确定会不会摸到冰冷的空气或是热腾…...

迅为iTOP-STM32MP157开发板板载4G接口(选配)_千兆以太网_WIFI蓝牙模块_HDMI_CAN_RS485_LVDS接口等

迅为ITOP-STM32MP157是基于ST的STM32MP157芯片开发的一款开发平台。在STM32MP157开发平台上&#xff0c;我们也做了比较多的创新&#xff0c;其中重要的一点就是&#xff0c;iTOP-STM32MP157核心板电源管理采用ST全新配套研制的PMIC电源管理芯片STPMU1A。为整个系统的稳定运行提…...

Android Choreographer 监控应用 FPS

Choreographer 是 Android 提供的一个强大的工具类&#xff0c;用于协调动画、绘制和视图更新的时间。它的主要作用是协调应用的绘制过程&#xff0c;以确保流畅的用户体验。Choreographer 也可以帮助我们获取帧时间信息&#xff0c;从而为性能监测和优化提供重要的数据支持。 …...

关于 mybatis-plus-boot-starter 与 mybatis-spring-boot-starter 的错误

不是知道你是否 出现过这样的错误 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 经过各种度娘&#xff0c;无非就是让你检查三种情况 情况一&#xff1a;mapper.xml没有按照传统的maven架构进行放置 情况二&#xff1a;mybatis的配置信…...

NLP 文本分类任务核心梳理

解决思路 分解为多个独立二分类任务将多标签分类转化为多分类问题更换 loss 直接由模型进行多标签分类 数据稀疏问题 标注更多数据&#xff0c;核心解决方案&#xff1a; 自己构造训练样本 数据增强&#xff0c;如使用 chatGPT 来构造数据更换模型 减少数据需求增加规则弥补…...

k8s中pod的创建过程和阶段状态

管理k8s集群 kubectl k8s中有两种用户 一种是登录的 一种是/sbin/nologin linux可以用密码登录&#xff0c;也可以用证书登录 k8s只能用证书登录 谁拿到这个证书&#xff0c;谁就可以管理集群 在k8s中&#xff0c;所有节点都被网络组件calico设置了路由和通信 所以pod的ip是可以…...

NSSCTF刷题篇1

js类型 [SWPUCTF 2022 新生赛]js_sign 这是一道js信息泄露的题目直接查看源码&#xff0c;有一个main.js文件点击之后&#xff0c;有一串数字和一段base64编码&#xff0c;解开base64编码得到这个编码为敲击码 解码在线网站&#xff1a;Tap Code - 许愿星 (wishingstarmoye.…...

[数据集][目标检测]棉花叶子病害检测数据集VOC+YOLO格式977张22类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;977 标注数量(xml文件个数)&#xff1a;977 标注数量(txt文件个数)&#xff1a;977 标注类别…...

产品经理面试整理-常见面试问题

以下是一些常见的产品经理面试问题及其解答思路。这些问题涵盖了产品管理的各个方面,包括战略、执行、数据分析、用户体验、跨团队合作等。在准备这些问题时,使用结构化的回答方式(如STAR法)能够帮助你更好地表达你的观点和经验。 1. 常见产品经理面试问题 1.1 你如何定义用…...

数据库(选择题)

基本概念 数据库&#xff08;DB&#xff09;&#xff1a;长期存储在计算机内的、有组织的、可共享的数据集合。 数据库管理系统&#xff08;DBMS&#xff09;&#xff1a;它是数据库的机构&#xff0c;是一个系统软件&#xff0c;负责数据库中的数据组织、数据操纵、数据维护…...

粒子向上持续瀑布动画效果(直接粘贴到记事本改html即可)

代码&#xff1a; 根据个人喜好修改即可 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>宽粒子向上…...

卷积神经网络(CNN):深度学习中的视觉奇迹

目录 一、什么是卷积神经网络&#xff1f; 二、CNN的核心组件 1. 卷积层&#xff08;Convolutional Layer&#xff09; 2. 激活函数&#xff08;Activation Function&#xff09; 3. 池化层&#xff08;Pooling Layer&#xff09; 4. 全连接层&#xff08;Fully Connected…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...