WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
} // 包了一层,把 parent 赋值给子 context
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{Context: parent}
} type cancelCtx struct {
Context mu sync.Mutex // protects following fields
done chan struct{} // created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
func propagateCancel(parent Context, child canceler) {
if parent.Done() == nil {
return // parent is never canceled
}
// 判断 parent 是否为 cancelCtx,timerCtx 也属于 cancelCtx,cancelCtx 有 child
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// cancelCtx 被 cancel() 的时候,会给err属性赋值
// parent has already been canceled
child.cancel(false, p.err)
} else { // parent 没有被 cancel()
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{} // 将 child 添加到 parent 中
}
p.mu.Unlock()
} else { // parent 的状态未确定
go func() {
select {
case <-parent.Done(): // 如果可以从 parent 中获取到值,说明 parent 被 cancel 了
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
} // 类型判断
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
for {
switch c := parent.(type) {
case *cancelCtx:
return c, true
case *timerCtx:
return &c.cancelCtx, true
case *valueCtx:
parent = c.Context
default:
return nil, false
}
}
}

分析下 WithCancel 返回的函数

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
} func (c *cancelCtx) cancel(removeFromParent bool, err error) {
// 被 cancel 的 cancelCtx 的 err 属性,必须被赋值
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // 已经被 cancel
}
c.err = err
if c.done == nil {
c.done = closedchan // 注意此处!!!
} else {
close(c.done) // close 后,select 可以不断获取到默认值
}
// close 掉所有 child
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock() if removeFromParent {
removeChild(c.Context, c)
}
} func removeChild(parent Context, child canceler) {
// 只有 cancelCtx 类型的 context,才有 child
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
} // closedchan is a reusable closed channel.
// closedchan init() 的时候就被 close 了,所以是可以通过 select 不断获取值的
var closedchan = make(chan struct{})
func init() {
close(closedchan)
}

go context 源码分析的更多相关文章

  1. Context源码分析

    我们做安卓开发,时时都在和Context打交道,那么Context到底是什么?有什么作用?如何与Application,Activity,Service等实例发生联系的?等等 Context是什么? ...

  2. Android源码分析-全面理解Context

    前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...

  3. requirejs源码分析: requirejs 方法–2. context.require(deps, callback, errback);

    上一篇 requirejs源码分析: requirejs 方法–1. 主入口  中的return context.require(deps, callback, errback);  调用的是make ...

  4. ABP源码分析二十四:Notification

    NotificationDefinition: 用于封装Notification Definnition 的信息.注意和Notification 的区别,如果把Notification看成是具体的消息 ...

  5. asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析

    这篇文章我们开始看一下ActionFilter,从名字上其实就大概知道ActionFilter就是Action上的Filter,对吧,那么Action上的Filter大概有几个呢??? 这个问题其实还 ...

  6. 深入理解 spring 容器,源码分析加载过程

    Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC ...

  7. jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

    Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...

  8. jQuery 2.0.3 源码分析 Deferred概念

    JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...

  9. jQuery-1.9.1源码分析系列(一)整体架构续

    这一节主要是jQuery中最基础的几个东东 2.    jQuery的几个基础属性和函数 a. jQuery.noConflict函数详解 在jQuery初始化的时候保存了外部的$和jQuery _j ...

随机推荐

  1. C#作业处理

    2019.9.8 作业要求: 将字符串加密,即将字符串中每个字符向后移动五个字符,并输出密文 解决方案: using System; using System.Collections.Generic; ...

  2. 范仁义web前端介绍课程---5、webstorm的下载安装

    范仁义web前端介绍课程---5.webstorm的下载安装 一.总结 一句话总结: webstorm破解版搜索:webstorm破解 site:52pojie.cn 编辑器随便选用,功能都差不多,哪 ...

  3. ISO/IEC 9899:2011 条款6.7.1——存储类说明符

    6.7.1 存储类说明符 语法 1.storage-class-specifier: typedef extern static _Thread_local auto register 约束 2.在一 ...

  4. 一、postman简介

    一.场景 1.开发接口的时候需要快速的调用接口,以便调试 2.测试的时候需要非常方便的调用接口,通过不同的参数去测试接口的输出 3.这些接口调用是需要保存下来的反复运行的 4.在运行过程中如果有断言( ...

  5. spring 依赖注入的3种方式

    在实际环境中实现IoC容器的方式主要分为两大类,一类是依赖查找,依赖查找是通过资源定位,把对应的资源查找回来:另一类则是依赖注入,而Spring主要使用的是依赖注入.一般而言,依赖注入可以分为3种方式 ...

  6. window下安装docker

    下载docker toolbox https://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/ 1,.双击安装DockerTool ...

  7. DBGrid 单击弹出PickList

    type   myGrid = class(TCustomGrid)   end; type   myInplaceEditList = class(TInplaceEditList)   end; ...

  8. 学习activiti的时候使用truncate命令清空表

    参考文档: https://blog.csdn.net/iw1210/article/details/79586033 https://www.cnblogs.com/hougang/p/mysql_ ...

  9. IE11的变化 navigator.userAgent中不再包含“MSIE”关键字

    IE升级了,让人好头疼,升级个东西,我们也要跟着升级,程序猿压力大呀.... 1.navigator.userAgent中不再包含“MSIE”关键字 2.用javascript的判断是否是IE11的方 ...

  10. AI - H2O - 安装与运行

    安装的要求 H2O的安装对操作系统.编程语言和浏览器有具体的要求. 详情请查看官方信息 下载H2O Downloading & Installing H2O Download 示例 - 在Ce ...