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. 深度学习面试题11:池化(same池化、valid池化、带深度的池化)

    目录 Same最大值池化 多深度的same池化 Same平均值池化 Valid池化 参考资料 池化(Pooling)操作与卷积类似,取输入张量的每个位置的矩形领域内的最大值或平均值作为该位置的输出. ...

  2. cropper手机使用实例

    cropper手机使用实例 一.总结 一句话总结: 启示:还是要多个相关的实例交叉使用,相互印证,查漏补缺,可以更加高效和方便和节约时间 二.Cropper.js从前台到后台的完整实例应用 转自或参考 ...

  3. 关于解决'\u'开头的字符串转中文的方法

    如果字符串是”\u70ed\u95e8\u94ed\u6587\u63a8\u8350”这种形式的字符串: python3的解决办法:字符串.encode(‘utf-8’).decode(‘unico ...

  4. 远程桌面工具mRemoteNG与Tsmmc

    一.Tsmmc.msc远程管理工具.1.下载链接:https://pan.baidu.com/s/1tV_xP-ITWyKKzAxLSlGxlw 密码:0jrt 将目录下的mstsmhst.dll.m ...

  5. vue+springboot上传和下载附件功能

    https://blog.csdn.net/qq_35867245/article/details/84325385 上传附件(服务端代码) 第一步:在application.yml中配置附件要上传的 ...

  6. zz SOLID (面向对象设计)

    SOLID (面向对象设计) 维基百科,自由的百科全书 跳到导航 跳到搜索 在程序设计领域, SOLID(单一功能.开闭原则.里氏替换.接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期[1] ...

  7. AndoridSQLite数据库开发基础教程(4)

    AndoridSQLite数据库开发基础教程(4) 安装SQLiteManager 以下是SQLiteManager的安装步骤: (1)双击下载的.exe文件,弹出SQLiteManager Setu ...

  8. linux(centos7.0以上版本)安装 mysql-5.7.24-linux-glibc2.12-x86_64.tar 版本的mysql

      1:查看 linux下是否有老版本的mysql(有删除) 查找old mysql:rpm -qa | grep mysql 卸载:卸载命令:rpm –ev {包名}——:rpm -ev mysql ...

  9. ISO/IEC 9899:2011 条款6.7.10——静态断言

    6.7.10 静态断言 语法 1.static-assert_declaration: _Static_assert    (    constant-expression    ,    strin ...

  10. ISO/IEC 9899:2011 条款6.7.8——类型定义

    6.7.8 类型定义 语法 1.typedef-name: identifier 约束 2.一个typedef名指定了一个可变修改的类型,然后它应该具有语句块作用域. 语义 3.在一个声明中,该声明的 ...