Context 是 Go 语言独有的设计,在其他编程语言中很少见到类似的概念,用一句话解释 Context 在 Go 语言中的作用就是:

Context 为同一任务的多个 goroutine 之间提供了 退出信号通知元数据传递的功能。

那么如果不用 Context,就不能在 Go 语言里实现多个 goroutine  间的信号通知和元数据传递了吗?答案是:简单场景下可以,在多层级 goroutine 的控制中就行不通了。

我们举一个例子来理解上面那段话

假如主协程中有多个任务,主协程对这些任务有超时控制;而其中任务1又有多个子任务,任务1对这些子任务也有自己的超时控制,那么这些子任务既要感知主协程的取消信号,也需要感知任务1的取消信号。

一个任务有多层goroutine

任务的 goroutine 层级越深,想要自己做退出信号感知和元数据共享就越难。

所以我们需要一种优雅的方案来实现这样一种机制:

  • 上层任务取消后,所有的下层任务都会被取消;

  • 中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务;

  • 可以线程安全地在 goroutine 之间共享元数据;

为此 Go 官方在1.7 版本就引入了 Context 来实现上面阐述的机制。

Context接口和类型间的关系

Go 的 context 包提供了对Context的接口定义和类型实现,我通过一张类图给大家描述下 context 提供的接口和类型。

通过上面的类图我们能获取到这些信息

  • 除了Context 接口外还定义了一个叫做 canceler 的接口,实现了它的类型即为带取消功能的 Context。

  • emptyCtx 什么属性也没有,啥也不能干。

  • valueCtx 只能携带一个键值对,且自身要依附在上一级 Context 上。

  • timerCtx 继承自 cancelCtx 他们都是带取消功能的 Context。

  • 除了emptyCtx,其他类型的 Context 都依附在上级 Context 上

看完这个类图,你可能会问 Context 是怎么实现任务在元数据间传递的呢?毕竟一个valueCtx只携带一个键值对。其实原理也很简单,它实现的 Value 方法能够在整个Context链路上查找指定键的值,直到回源到根 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的树形结构

上面这个示意图可以让我们更明白整个Context链路可能的全景面貌。

看源码的同学,重点关注用 WithCancel、WithDeadline这些方法里对propagateCancel的调用

propagateCancel中会在祖先Context节点中找到可取消的Context,把自己维护到祖先的children属性里

经过这个结构设计,如果要在整个任务链路上取消某个cancelCtx时,就能做到既取消自己,也能通知下级 cancelCtx 进行取消,同时还不会影响到上级和同级的其他节点。

现在让我们再回到开头那个例子,有了 Context 之后,我们的任务会变成什么样呢?

携带Context的多层级goroutine

我们让每个 goroutine 都携带了 Context ,那些做子任务的 goroutine 只要监听了这些子 cancelCtx 也就能收到信号,结束自己的运行,即通过Context 完成上级 goroutine 对下级 goroutine 的取消控制。

面对不同层级的 goroutine 的取消条件不同的情况,代码里只需要监听传递到 goroutine 里的 Context 就能做到,免除了监听多个信号的繁琐。

针对Context的使用建议,Go官方提到了下面几点:

  1. 不要将 Context 塞到结构体里。直接将 Context 类型作为函数的第一参数,而且一般都命名为 ctx。

  2. 不要向函数传入一个 nil 的 context,如果你实在不知道传什么,标准库的TODO方法给你准备好了一个 emptyCtx。

  3. 不要把本应该作为函数参数的类型塞到 context 中,context 存储的应该是一些在 goroutine 共享的数据,比如Server的信息等等。

第一点的前半部分我觉得说的很牵强,比如在官方的 net/http 包里就把 Context 放到了 Request的结构体里,其他几点确实是需要注意的地方。

好了,今天是不卷源码的一天,我用通俗的语言和几张图示向你展示了Context的设计理念和它在Go语言里起到的重要作用,如果你能喜欢这种形式,请不要吝啬你的点赞和在看,感谢你的支持。

如果想看 context 的源码分析,推荐码农桃花源写的文章:深度解密Golang  Context , 需要注意的是文章讲解的代码是1.9版本的,后来几个版本context的源码有做微调,不过对理解 Context 整个的实现原理没有影响。

文章推荐:

图文结合,白话Go的垃圾回收原理

解密Go协程的栈内存管理

Context是怎么在Go语言中发挥关键作用的相关推荐

  1. 智芯传感微差压气体压力传感器在CPAP治疗中发挥关键作用

    睡眠时打鼾(打呼噜)是一种比较常见的现象,通常人们认为"打鼾"就意味着这个人睡的比较香.但实际上并非如此. 近日,一则 #四个月宝宝被爸爸鼾声吵到空气挥拳# 被冲上微博热搜榜第一名 ...

  2. 大咖说|阿里巴巴副总裁陈龙:数字技术将在绿色低碳转型中发挥关键作用

    数字技术会带来生产和能源消费效率提高,帮企业更好地衡量和管理碳足迹,更重要的是,数字技术能超越时空边界,构建创新的绿色生活方式. 嘉宾:阿里巴巴集团副总裁.阿里巴巴集团可持续发展管理委员会主席 数字技 ...

  3. “木马源”攻击影响多数编程语言的编译器,将在软件供应链攻击中发挥巨大作用...

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 专栏·供应链安全 数字化时代,软件无处不在.软件如同社会中的"虚拟人",已经成为支撑社会正常运转的最基本元素之一,软件的安全 ...

  4. 浅谈软件开发工具CASE在软件项目开发中发挥的作用认识

    浅谈软件开发工具CASE在软件项目开发中发挥的作用认识 内容摘要:阐述了CASE工具作为 一种开发环境在软件项目开发中所起到的开发及管理作用.CASE工具实际上是把原先由手工完成的开发过程转变为以自动 ...

  5. 太极链在协商共识协议中发挥的作用

    太极链在协商共识协议中发挥的作用.人类本质上是复杂的,他们的行为也是如此.因此,处理许多交互的协议是一项相当具有挑战性的任务.每个人都受到一系列独特因素的刺激,因此对刺激的反应也不同.因此,找出适用于 ...

  6. 精诚MES智能制造公司在数字化制造中发挥核心作用

    互联设备一直是制造执行系统的关键.由于信息管理系统专注于制造工厂车间的数据收集和控制,它们最初在自己的基础设施中发展,以利用技术 - 而不是面向业务的 - 硬件和软件,能够在所需的短时间内处理来自工厂 ...

  7. C语言的格式输出 C语言中字符的作用:

    这是C语言的格式输出,%c,%y这些代表你要输出的数据的数据类型:%d 表示输出十进制有符号的整数. 1.%u 十进制无符号整数. 2.%f 表示输出浮点数. 3.%s表示输出 字符串. 4.%c表示 ...

  8. 易基因|文献科普:DNA甲基化测序揭示DNMT3a在调控T细胞同种异体反应中的关键作用

    大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因. 2022年7月1日,美国约翰霍普金斯大学西德尼基梅尔综合癌症中心Christopher J. Gamper 和 Kenneth R. C ...

  9. 索尼:声控将在VR中发挥大作用

    (52VR开发网2017年5月17日讯)索尼的Richard Marks作为PlayStation VR(PSVR)头显的最早开发人员之一,并为公司的无数其他外围设备做了大量工作,对于未来的科技知之甚 ...

最新文章

  1. 简单页面跳转生命周期(简单清晰)
  2. python3 执行系统命令_Python3 执行系统命令并获取实时回显功能
  3. [vue] vue边界情况有哪些?
  4. 直播回顾:准确性提升到 5 秒级,ssar 独创的 load5s 指标有多硬核?| 龙蜥技术
  5. iphone如何信任软件_苹果企业开发者证书成漏洞 盗版商发布破解版iPhone应用
  6. mysql 删除创建表分区_创建,增加,删除mysql表分区
  7. 内联函数inline,无比节省开销的
  8. JVM 可设置最大内存
  9. linux基本命令的使用原理,linux基本常用命令及其使用方法
  10. python机器学习库sklearn——集成方法(Bagging、Boosting、随机森林RF、AdaBoost、GBDT)
  11. ios学习:UIToolBar的单独使用
  12. php代码审计_代码审计|PHP反序列化初识
  13. java工厂模式_java工厂模式
  14. Angular 图片懒加载
  15. YOLO学习笔记——第三篇YOLOv3(含FPN网络解析)
  16. Cookie用法大全
  17. W ndows7安装Hp1020,Windows7系统怎么安装惠普hp1020打印机
  18. 使用CUPS打印服务
  19. html中div排版布局
  20. FPGA 视频处理中外部SDRAM的作用

热门文章

  1. admin- 源码解析(流程)
  2. RabbitMQ 入门 Helloworld
  3. 微信成为开发者C#代码
  4. Fckeditor漏洞汇总
  5. Exchange笔记之Exchange Server 2003前端后端部署
  6. 记录一下flex布局左边固定,右边100%
  7. Swoole 2019 :化繁为简、破茧成蝶
  8. College student reflects on getting started in open source(二)
  9. ipmi对于统一硬件管理的意义
  10. SpringMVC拦截器工作流程图