Context是怎么在Go语言中发挥关键作用的
Context 是 Go 语言独有的设计,在其他编程语言中很少见到类似的概念,用一句话解释 Context 在 Go 语言中的作用就是:
Context 为同一任务的多个 goroutine 之间提供了 退出信号通知
和 元数据传递
的功能。
那么如果不用 Context,就不能在 Go 语言里实现多个 goroutine 间的信号通知和元数据传递了吗?答案是:简单场景下可以,在多层级 goroutine 的控制中就行不通了。
我们举一个例子来理解上面那段话
假如主协程中有多个任务,主协程对这些任务有超时控制;而其中任务1又有多个子任务,任务1对这些子任务也有自己的超时控制,那么这些子任务既要感知主协程的取消信号,也需要感知任务1的取消信号。
任务的 goroutine 层级越深,想要自己做退出信号感知和元数据共享就越难。
所以我们需要一种优雅的方案来实现这样一种机制:
上层任务取消后,所有的下层任务都会被取消;
中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务;
可以线程安全地在 goroutine 之间共享元数据;
为此 Go 官方在1.7 版本就引入了 Context 来实现上面阐述的机制。
Go 的 context 包提供了对Context的接口定义和类型实现,我通过一张类图给大家描述下 context 提供的接口和类型。
通过上面的类图我们能获取到这些信息
除了Context 接口外还定义了一个叫做 canceler 的接口,实现了它的类型即为带取消功能的 Context。
emptyCtx 什么属性也没有,啥也不能干。
valueCtx 只能携带一个键值对,且自身要依附在上一级 Context 上。
timerCtx 继承自 cancelCtx 他们都是带取消功能的 Context。
除了emptyCtx,其他类型的 Context 都依附在上级 Context 上
看完这个类图,你可能会问 Context 是怎么实现任务在元数据间传递的呢?毕竟一个valueCtx只携带一个键值对。其实原理也很简单,它实现的 Value 方法能够在整个Context链路上查找指定键的值,直到回源到根 Context。
通过查找Context 携带的键值对的示意图我们能看到Context链路的根节点是一个 emptyCtx,这也就是emptyCtx 什么个功能也不提供的原因,它是用来作为根节点而存在的。
每次要在Context链路上增加要携带的键值对时,都要在上级Context的基础上新建一个 valueCtx 存储键值对,切只能增加不能修改,读取 Context 上的键值又是一个幂等的操作,所以 Context 就这样实现了线程安全的数据共享机制,且全程无锁,不会影响性能。
那么 “上层任务取消后,所有的下层任务都会被取消”,“中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务” 这两个取消信号同步的关键点, Context 又是怎么实现的呢?
下文把cancelCtx,timerCtx统称带取消功能的Context,且示意图中也会用 cancelCtx 这个标记代表他们。
首先在 创建 带取消功能的Context
的时候还是要在父级Context节点的基础上创建,保持整个Context链路的连续性。除此之外还会在Context链路中找到上一个带取消功能的 Context,把自己加入到它的 children 列表里。
这样在整个Context链路里,除了父子Context之间有直接关联外,可取消的Context还会通过维护自身携带的children 属性建立自己与下级可取消Context之间的关联。
上面这个示意图可以让我们更明白整个Context链路可能的全景面貌。
看源码的同学,重点关注用 WithCancel、WithDeadline这些方法里对propagateCancel的调用
propagateCancel中会在祖先Context节点中找到可取消的Context,把自己维护到祖先的children属性里
经过这个结构设计,如果要在整个任务链路上取消某个cancelCtx时,就能做到既取消自己,也能通知下级 cancelCtx 进行取消,同时还不会影响到上级和同级的其他节点。
现在让我们再回到开头那个例子,有了 Context 之后,我们的任务会变成什么样呢?
我们让每个 goroutine 都携带了 Context ,那些做子任务的 goroutine 只要监听了这些子 cancelCtx 也就能收到信号,结束自己的运行,即通过Context 完成上级 goroutine 对下级 goroutine 的取消控制。
面对不同层级的 goroutine 的取消条件不同的情况,代码里只需要监听传递到 goroutine 里的 Context 就能做到,免除了监听多个信号的繁琐。
针对Context的使用建议,Go官方提到了下面几点:
不要将 Context 塞到结构体里。直接将 Context 类型作为函数的第一参数,而且一般都命名为 ctx。
不要向函数传入一个 nil 的 context,如果你实在不知道传什么,标准库的TODO方法给你准备好了一个 emptyCtx。
不要把本应该作为函数参数的类型塞到 context 中,context 存储的应该是一些在 goroutine 共享的数据,比如Server的信息等等。
第一点的前半部分我觉得说的很牵强,比如在官方的 net/http 包里就把 Context 放到了 Request的结构体里,其他几点确实是需要注意的地方。
好了,今天是不卷源码的一天,我用通俗的语言和几张图示向你展示了Context的设计理念和它在Go语言里起到的重要作用,如果你能喜欢这种形式,请不要吝啬你的点赞和在看,感谢你的支持。
如果想看 context 的源码分析,推荐码农桃花源写的文章:深度解密Golang Context , 需要注意的是文章讲解的代码是1.9版本的,后来几个版本context的源码有做微调,不过对理解 Context 整个的实现原理没有影响。
文章推荐:
图文结合,白话Go的垃圾回收原理
解密Go协程的栈内存管理
Context是怎么在Go语言中发挥关键作用的相关推荐
- 智芯传感微差压气体压力传感器在CPAP治疗中发挥关键作用
睡眠时打鼾(打呼噜)是一种比较常见的现象,通常人们认为"打鼾"就意味着这个人睡的比较香.但实际上并非如此. 近日,一则 #四个月宝宝被爸爸鼾声吵到空气挥拳# 被冲上微博热搜榜第一名 ...
- 大咖说|阿里巴巴副总裁陈龙:数字技术将在绿色低碳转型中发挥关键作用
数字技术会带来生产和能源消费效率提高,帮企业更好地衡量和管理碳足迹,更重要的是,数字技术能超越时空边界,构建创新的绿色生活方式. 嘉宾:阿里巴巴集团副总裁.阿里巴巴集团可持续发展管理委员会主席 数字技 ...
- “木马源”攻击影响多数编程语言的编译器,将在软件供应链攻击中发挥巨大作用...
聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 专栏·供应链安全 数字化时代,软件无处不在.软件如同社会中的"虚拟人",已经成为支撑社会正常运转的最基本元素之一,软件的安全 ...
- 浅谈软件开发工具CASE在软件项目开发中发挥的作用认识
浅谈软件开发工具CASE在软件项目开发中发挥的作用认识 内容摘要:阐述了CASE工具作为 一种开发环境在软件项目开发中所起到的开发及管理作用.CASE工具实际上是把原先由手工完成的开发过程转变为以自动 ...
- 太极链在协商共识协议中发挥的作用
太极链在协商共识协议中发挥的作用.人类本质上是复杂的,他们的行为也是如此.因此,处理许多交互的协议是一项相当具有挑战性的任务.每个人都受到一系列独特因素的刺激,因此对刺激的反应也不同.因此,找出适用于 ...
- 精诚MES智能制造公司在数字化制造中发挥核心作用
互联设备一直是制造执行系统的关键.由于信息管理系统专注于制造工厂车间的数据收集和控制,它们最初在自己的基础设施中发展,以利用技术 - 而不是面向业务的 - 硬件和软件,能够在所需的短时间内处理来自工厂 ...
- C语言的格式输出 C语言中字符的作用:
这是C语言的格式输出,%c,%y这些代表你要输出的数据的数据类型:%d 表示输出十进制有符号的整数. 1.%u 十进制无符号整数. 2.%f 表示输出浮点数. 3.%s表示输出 字符串. 4.%c表示 ...
- 易基因|文献科普:DNA甲基化测序揭示DNMT3a在调控T细胞同种异体反应中的关键作用
大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因. 2022年7月1日,美国约翰霍普金斯大学西德尼基梅尔综合癌症中心Christopher J. Gamper 和 Kenneth R. C ...
- 索尼:声控将在VR中发挥大作用
(52VR开发网2017年5月17日讯)索尼的Richard Marks作为PlayStation VR(PSVR)头显的最早开发人员之一,并为公司的无数其他外围设备做了大量工作,对于未来的科技知之甚 ...
最新文章
- 简单页面跳转生命周期(简单清晰)
- python3 执行系统命令_Python3 执行系统命令并获取实时回显功能
- [vue] vue边界情况有哪些?
- 直播回顾:准确性提升到 5 秒级,ssar 独创的 load5s 指标有多硬核?| 龙蜥技术
- iphone如何信任软件_苹果企业开发者证书成漏洞 盗版商发布破解版iPhone应用
- mysql 删除创建表分区_创建,增加,删除mysql表分区
- 内联函数inline,无比节省开销的
- JVM 可设置最大内存
- linux基本命令的使用原理,linux基本常用命令及其使用方法
- python机器学习库sklearn——集成方法(Bagging、Boosting、随机森林RF、AdaBoost、GBDT)
- ios学习:UIToolBar的单独使用
- php代码审计_代码审计|PHP反序列化初识
- java工厂模式_java工厂模式
- Angular 图片懒加载
- YOLO学习笔记——第三篇YOLOv3(含FPN网络解析)
- Cookie用法大全
- W ndows7安装Hp1020,Windows7系统怎么安装惠普hp1020打印机
- 使用CUPS打印服务
- html中div排版布局
- FPGA 视频处理中外部SDRAM的作用