Context中的cancelCtx的难点大白话解析

文章目录

  • 前言
  • Context家族
    • emptyCtx
    • cancelCtx
    • timerCtx
    • valueCtx
  • cancelCtx初始化
  • cancelCtx取消方法
  • 参考

前言

context,学习golang的同学应该都接触过这个。就一个context.go的简单文件涵盖了很多golang的知识点,从设计模式到线程安全都有涵盖,当然一切的根本就是接口Context,但是其精髓应该是cancelCtx(到目前为止,不确保官方继续实现新的context)


接下来,不会大面积铺开对context进行讲解,主要解析cancelCtx的几个知识点(因为其核心也就那几个,了解了cancelCtx,timerCtx很快就了解了的)。
肯定会结合源码来讲,当然也都是大白话。我会通过引入族群的一些设定来讲解context,当然不了解设定的也可以看作是爷爷、爸爸 、孩子。
因为设定的问题,会有些大白话。大家谨慎食用。
还是要建议先自行去了解下context,有个初步的认识,而本文只希望能为大家拨开迷雾,彻底通透。

Context家族

种族名:context
特征:啥也不是能力(emptyCtx)、断绝能力(cancelCtx) 、延时断绝能力(timerCtx)、存储标记能力(valueCtx);
始祖:background;
设定:

  • 每个context个体只能拥有其中一种能力
  • 每个个体都会传承其父辈,且不会忘记他的上一辈

emptyCtx

啥也不是能力。没啥能力,是始祖之能力,后代中也会出现啥也不会只能啃老的。后代这种人我们一般都叫他们todo。

cancelCtx

断绝能力。为保全父辈,保全整个族群。可以在必要时刻将自己及自己的子嗣全部与族群脱离关系。所以拥有这种cancelCtx的,其时刻会带着一个记着自己子嗣的小本本( children map[canceler]struct{} ),什么时候自己又添子添孙啦,他就会在这个本子上记下来。(子孙:好可怕)

type cancelCtx struct {Contextmu       sync.Mutex            // protects following fieldsdone     chan struct{}         // created lazily, closed by first cancel callchildren map[canceler]struct{} // set to nil by the first cancel callerr      error                 // set to non-nil by the first cancel call
}

timerCtx

延时断绝能力。这种能力的人只能是从cancelCtx中产出,所以其是cancel一脉的(难怪都那么烈性)也就是说,其父辈必须是timerCtx或者cancelCtx类型的。
当然与cancelCtx不同的是,这种会提前设定好自己牺牲的时间,包括子孙。(可能是怕自己中途反悔不想死吧)

// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {cancelCtxtimer *time.Timer // Under cancelCtx.mu.deadline time.Time
}

valueCtx

存储标记能力。这个就是在这个族群中担任的是存储管理的能力。

cancelCtx初始化

众所周知,我们初始化cancelCtx用下面这个方法

//通过这个方法,context族群可以生产出拥有大无畏自我牺牲的精神的cancelCtx,
//返回值:生产出的自己,还有个CancelFunc方法,这个就是他把自我断绝的权利交了出来
//自己的存在是死是活一切由天(coder)决定,这里我们的CancelFunc方法会在下文细说
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {c := newCancelCtx(parent)propagateCancel(parent, &c)return &c, func() { c.cancel(true, Canceled) }
}// 生产仪式必要环节,必须要时刻记住自己的父辈,不能忘本。当然当与父辈断绝关系的时候,父辈的context是不可用的了
func newCancelCtx(parent Context) cancelCtx {return cancelCtx{Context: parent}
}// newCancelCtx只是生产出一个基本的自己,但是要根据自己的情况,比如自己父辈的情况,来做一些细致调整
func propagateCancel(parent Context, child canceler) {if parent.Done() == nil {return // 父辈都已经决定离开了,自己还有甚么存在的意义,为了安全起见,就不会再与context族群的这一支有任何关联。至于取消权由天定夺}//找寻自己的父辈,谁有能力来记住自己,因为既然自己有一个小本本来记自己的子孙,那么自己的父辈,甚至爷爷辈,肯定也有记小本本能力的,让他来记住自己,起码让自己与父辈共存亡if p, ok := parentCancelCtx(parent); ok {p.mu.Lock()if p.err != nil {//当父辈拥有可以记录自己子孙能力的人的时候,当他看到这个父辈出现了异常,准备自我牺牲,那么我们的主角也会将子孙遣散。并不再与这个父辈牵连任何关系// parent has already been canceledchild.cancel(false, p.err)} else {// 如果父辈一切正常,但是父辈还没有开始记录子孙,那么正好,给父节点买个本子开始把自己名字填上去。if p.children == nil {p.children = make(map[canceler]struct{})}p.children[child] = struct{}{}}p.mu.Unlock()} else {//如果遇到empty的父辈或者畸变的父辈,那么这个时候需要一个随行监察员时刻监察着主角和这个啥也不会的父辈关系,这里就相当于让这个监察员来维系着主角与这个父辈的关系,因为我们这个父辈啥也不是啊,是empty(background/todo)或者畸变的,他可能没有小本本,无法记录自己的子孙。这个时候就需要这个检察员来帮忙记录了。当父辈结束的时候(被外部调用父辈的cancelFunc),我们的主角大义凛然也跟着遣散自己的子孙。然后这个监察员工作结束了,只要监察员一走,他们的之间没有任何联系了,我们的主角就是个游荡人的人,生死存亡就靠大自然了(gc),//当然 我们的主角自己也觉得自己该结束了,那么监察员也没有存在的必要了。他不需要再记了,这样还减小请监察员的开支go func() {select {case <-parent.Done():child.cancel(false, parent.Err())case <-child.Done():}}()}
}
// 找寻自己的父辈,谁有能力来记住自己,因为既然自己有一个小本本来记自己的子孙,那么自己的父辈,甚至爷爷辈,肯定也有记小本本能力的,让他来记住自己,起码让自己与父辈共存亡,
// 当发现父辈是存储管理类型,他会继续去找直到找到拥有记录自己子孙能力的,比如cancelctx或者timerCtx。
//不过当他在找寻中遇到有啥也不是能力的父辈或者是那种畸变的父辈(coder自定义的继承关系 比如: CommonCtx{Context},这个CommonCtx类型就是畸变的,他的变量也能继承context,但是他并没有什么功能。只有官方钦点的几个能力才是正统能力)
//那么就会返回nil
func parentCancelCtx(parent Context) (*cancelCtx, bool) {for {switch c := parent.(type) {case *cancelCtx:return c, truecase *timerCtx:return &c.cancelCtx, truecase *valueCtx:parent = c.Contextdefault:return nil, false}}
}

cancelCtx取消方法

对自己狠一些,取消自己,让自己脱离这个族系;注意,我们context族群的牺牲功能并不是将自己变为nil,而是说将自己与父辈的关系决裂,然后也让自己的子孙也做跟自己同样的事情。使自己脱离出这个族群,游荡在golang大陆上,直到被自然(gc)征服,所以我们的这里的cancel方法只是将我们的当前主角及其子孙脱离出这个族群。

// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {if err == nil {panic("context: internal error: missing cancel error")}c.mu.Lock()if c.err != nil {c.mu.Unlock()return // already canceled}c.err = err// 在执行cancel的时候,会标记给自己通过自身的一个喇叭channel,告诉外界,自己已经结束了(closechan)if c.done == nil {c.done = closedchan} else {close(c.done)}for child := range c.children {// NOTE: acquiring the child's lock while holding parent's lock.// 开始责令自己的子孙也要做和自己相同的事情child.cancel(false, err)}//扔掉小本本,自己也没必要了c.children = nilc.mu.Unlock()//是否考虑将自己从自己父辈小本本中抹除if removeFromParent {removeChild(c.Context, c)}
}// removeChild removes a context from its parent.
// 将自己从父辈的小本本中抹除
func removeChild(parent Context, child canceler) {//找寻记录自己名字在小本本上的长辈,(在诞生于这个族群的时候,也是通过这个方式来让长辈记住自己的)p, ok := parentCancelCtx(parent)if !ok {return}p.mu.Lock()//找到这个长辈,告知他要他将自己的名字从小本本中删除if p.children != nil {delete(p.children, child)}p.mu.Unlock()
}

参考

深度解密Go语言之context.

大白话解析Context中的cancelCtx相关推荐

  1. 从源码角度解析Android中APK安装过程

    从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...

  2. java 获取spring对象数组_解析Java中如何获取Spring中配置的bean

    解析Java中如何获取Spring中配置的bean Java中如何获取Spring中配置的bean?下面是由百分网小编为大家整理的解析Java中如何获取Spring中配置的bean,喜欢的可以收藏一下 ...

  3. 解析xml_Mybatis中mapper的xml解析详解

    上一篇文章分析了mapper注解关键类MapperAnnotationBuilder,今天来看mapper的项目了解析关键类XMLMapperBuilder. 基础介绍 回顾下之前是在分析config ...

  4. 教你如何完全解析Kotlin中的注解

    简述: 从这篇文章将继续开始探索Kotlin中的一些高级的内容,之前有着重探讨了Kotlin的泛型以及泛型型变等内容.现在我们一起来看下Kotlin中的注解.Kotlin中的注解是100%与Java注 ...

  5. Go-ethereum 解析ethersjs中产生的签名信息

    Go-ethereum 解析ethersjs中产生的签名信息 在签名验证的过程中,我们判断签名正确的前提是,签名解析后的公钥,和发起这次动作的人是同一个公钥. 我们解析签名的需要知道,签名的消息,签名 ...

  6. 装饰者模式及其在 Android Context 中的应用

    一.装饰者模式 1.1 定义 装饰者模式又名包装(Wrapper)模式.它可以在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象上,从而实现动态拓展对象的功能.它是通过创建一个包装对象,也 ...

  7. python3 urlopen_扣丁学堂解析Python3中urlopen()使用详解

    扣丁学堂解析Python3中urlopen()使用详解 2018-07-23 13:31:14 1318浏览 在现如今,2018年Python一直属于IT行业中比较热门技术,那么今天扣丁学堂Pytho ...

  8. 区块链 以太坊 交易结构、执行、存储 解析 交易中为什么没有包含发送者地址这条数据

    一. 交易的结构 1. Transaction结构 交易结构定义在 core/types/transaction.go 中: type Transaction struct {//交易数据data t ...

  9. 大白话解析Apriori算法python实现(含源代码详解)

    大白话解析Apriori算法python实现(含源代码详解) 一.专业名词解释 二.算法思路 三.python代码实现 四.Aprioir的优点.缺点及改进方法 本文为博主原创文章,转载请注明出处,并 ...

最新文章

  1. Android视图绘制逻辑与思想(一):attachToRoot到底有什么作用?
  2. 智能家居(草纲)v0.1
  3. C指针原理(7)-C内嵌汇编
  4. ffplay.c学习-7-以音频同步为基准
  5. C语言 fclose 函数 - C语言零基础入门教程
  6. 安卓学习笔记18:常用控件 - 按钮、图像视图和图像按钮
  7. ERC223以太坊通证标准
  8. 【CodeForces】700 D. Huffman Coding on Segment 哈夫曼树+莫队+分块
  9. html5鼠标载入弹出信息框,js实现自定义弹出对话框(弹窗)可拖拽
  10. 编译OpenJDK8 b232版本
  11. 阿里巴巴招聘操作系统研发专家
  12. GitHup使用指南——安装及上传
  13. RoboWare Studio使用的部分问题
  14. 摄像头防水性能测试软件,手机摄像头防水测试/气密性检测方法分享
  15. 测试睡眠质量软件,测睡眠质量的app排行榜:推荐10款有趣又有效的睡眠APP
  16. 【MOS】RAC 环境中 gc block lost 和私网通信性能问题的诊断 (文档 ID 1674865.1)
  17. 驱动器控制模式功能简介(CSP,CSV,CST等)
  18. MacTex 使用教程
  19. 同源策略是什么,有何作用
  20. 不同平台下移植x264

热门文章

  1. 【大学物理·恒定电流的磁场】磁场对载流导线的作用
  2. Sketch教程|Sketch图层如何使用?如何使用Sketch画板?
  3. Life feelings--6--有一天感到没有动力了怎么办?--怎样找到持久的热情?
  4. python基本输入输出函数有_python基本输入输出函数与变量类型
  5. Robomaster:大风车识别可视化(记录一下)
  6. iOS 使用Zebra打印机打印标签
  7. 洛谷 P5664 Emiya 家今天的饭【dp】
  8. 千岛湖-印象中的天堂游记
  9. 7-2 换硬币 (20分)
  10. linux操作系统实验教程费翔林,实验一操作系统接口实验.doc