cancelCtx实现
cancelCtx 也是典型的装饰器模式:在已有Context 的基础上,加上取消的功能。
核心实现:
-
Done方法是通过类似于 double-check 的机制写的。这种原子操作和锁结合的用法比较罕见。(思考:能不能换成读写锁?)
-
利用 children来维护了所有的衍生节点,难点就在于它是如何维护这个衍生节点。
double-check-1:
done是一个原子变量,有load()方法。如果channel不为nil,即放返回。
double-check-2:
为nil则使用锁,加载channel并store到原子变量。
children: 核心是儿子把自己加进去父亲的children字段里面。
但是因为Context里面存在非常多的层级,所以父亲不一定是 cancelCtx,因此本质上是找最近属于 cancelCtx 类型的祖先,然后儿子把自己加进去。
cancel就是遍历children,挨个调用cancel。然后儿子调用孙子的cancel,子子孙孙无穷匮也。
parentCancelCtx 寻找第一个待cancel的父辈context,并加入children。
cancel
核心的cancel方法,做了两件事:
- 遍历所有的children
- 关闭done这个channel:这个符合谁创建谁关闭的原则‘
(利用了被关闭的channel永远能读到零值的特性,完成信号的发放)
timerCtx实现
timerCtx也是装饰器模式:在已有cancelCtx的基础上增加了超时的功能。
实现要点:
- WithTimeout 和 WithDeadline 本质一样WithDeadline 里面,
- 在创建 timerCtx 的时候利用 time.AfterFunc 来实现超时
context包使用注意
- 一般只用做方法参数,而且是作为第一个参数;
- 所有公共方法,除非是 util, helper 之类的方法(纯粹工具类,计算不会花费很长时间的 ) ,否则都加上 context 参数。
- 注意当与第三方,如调用方、数据库、Redis等打交道,一定要加context参数,以便于超时控制、链路控制等。
- 不要用作结构体字段,除非你的结构体本身也是表达一个上下文的概念。
(如HttpRequest 可以加到结构体字段)
context 提问要点
context.Context 使用场景:上下文传递(KV)和控制
context.Context 原理:
- 父亲如何控制儿子:通过儿子主动加入到父亲的children里面,父亲只需要遍历就可以。
- valueCtx和timeCtx的原理(timeCtx在cancelCtx基础上加time.AfterFunc)
context四个核心方法API使用场景,在什么时候使用,有什么样的特性。关注一下。
context四个接口核心方法,如Err的返回值。