核心结构
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Context接口的四个方法主要有下列用途:
Done
方法和Err
方法主要用于可Cancel的Context:type cancelCtx struct
Value
方法用于带值的Context:type valueCtx struct
Deadline
方法用于继承了cancelCtx
的有deadline的Context:type timerCtx struct
- 最后,
cancelCtx
改写了Value
方法,以使此方法可以判断自身或长辈(父辈、祖辈一直向上)是否为cancelCtx
类型,与这个操作相关的是包变量var cancelCtxKey int
非零,空Context
Context包中定义了最简单的非零,空Context如下:
type emptyCtx int
注:
在emptyCtx
的定义处有注释如下:It is not struct{}, since vars of this type must have distinct addresses.
怎么理解emptyCtx
不能定义为struct{}
?
因为当struct没有元素的时候,两个不同的zero-size变量在内存中可能有相同的地址,这就导致后面定义的两个不同的Context——background
和todo
——可能有相同的内存地址
参考:
https://go.dev/ref/spec#Size_and_alignment_guarantees
https://cloud.tencent.com/developer/article/1918419
emptyCtx
满足Context接口,几个方法的返回值也基本都是对应类型的零值
而由于emptyCtx
本身并未被导出,因此Context基于此创建了两个不同的Context:
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
这两个Context分别被函数Background
和TODO
导出
这两个Context都是以下特征:
- 非零,空
- 不被取消
- 没有值
- 没有deadline
其中:
background
主要被用在main函数、初始化、测试等处,作为后续请求的顶级上下文todo
主要被用在暂不清楚使用哪种上下文,或者当前函数环境还为被扩展为可接收上下文时临时使用
valueCtx
valueCtx
定义如下:
type valueCtx struct {
Context
key, val interface{}
}
除了父辈Context,新建一个valueCtx
就带一个键值对
Value函数为
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}
cancelCtx
cancelCtx
定义如下:
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done atomic.Value // of 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
}
其中canceler
定义如下:
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
cancelCtx
的核心用法是:
- 查询最近的可cancel长辈(用到了Done方法,追溯到最近的
cancelCtx
类型长辈),没有就不做继承操作 - 如果有可cancel长辈,则将自己放进其
Children
的map中,并监听其Cancel信号进行自动cancel(注意,如果长辈是用户自定义的canceler
,可能没有children map
,则会额外启动一个goroutine来监听长辈的Done
方法) - 如果手动cancel被调用,则对自己的
Children
进行cancel调用,并判断是否需要将自己从长辈的children中移除
注:
上文提到使用cancelCtxKey
和改写的Value
方法获取cancelCtx
长辈,就是在需要获取cancelCtx
长辈时使用cancelCtxKey
调用Value
:parent.Value(&cancelCtxKey)
而
cancelCtx
的Value
定义如下:func (c *cancelCtx) Value(key interface{}) interface{} { if key == &cancelCtxKey { return c } return c.Context.Value(key) }
区别于
emptyCtx
和valueCtx
的Value
方法,其调用仅可能在cancelCtx
长辈中获得返回值,于是达到了获取最近cancelCtx
长辈的目的
timerCtx
timerCtx
定义如下:
type timerCtx struct {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
timerCtx
继承cancelCtx
,主要增加了计时器用来自动触发cancelCtx
的cancel
方法
分享
最后分享一篇比较详细的Context分析文档:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。