context 主要分两种context 分为valueContext 和 cancelContext
value context 的结构如下
type valueCtx struct {
Context
key, val any
}
// 新建一个value Context
func WithValue(parent Context, key, val any) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
// 查找一个value
func (c *valueCtx) Value(key any) any {
if c.key == key {
return c.val
}
return value(c.Context, key)
}
func value(c Context, key any) any {
for { // 类似于递归
switch ctx := c.(type) {
case *valueCtx:
if key == ctx.key {
return ctx.val
}
c = ctx.Context
case *cancelCtx:
if key == &cancelCtxKey {
return c
}
c = ctx.Context
case *timerCtx:
if key == &cancelCtxKey {
return ctx.cancelCtx
}
c = ctx.Context
case *emptyCtx:
return nil
default:
return c.Value(key)
}
}
}
查找value的逻辑是:先查自己的context key 是不是想等,如果没有就递归的去查他的parent 可以想象成一棵树,查找可以自底向上查找,但不能从上向下查找。换句话说,父节点看不到子节点设置的值,但子节点可以看到父节点的值
接口
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
type canceler interface {
cancel(removeFromParent bool, err, cause error)
Done() <-chan struct{}
}
cancelContext 当他被取消时,也会取消所有的孩子节点
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
cause error // set to non-nil by the first cancel call
}
cancel 函数 总体流程如下,删除了一些代码,留了一些核心流程
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
close(c.done)
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err, cause)
}
c.children = nil
if removeFromParent {
removeChild(c.Context, c)
}
}
func parentCancelCtx(parent Context) (cancelCtx, bool) 从parent位置沿着父级不断的向上查找,直到遇到第一个cancelCtx或者不存这样的*cancelCtx