go context

发布时间 2023-07-15 10:08:54作者: 王景迁

使用场景

在协程之间传递上下文

context接口

type Context interface {
    // 返回绑定当前context的任务取消的截止时间
    // 如果没有设定期限,将返回ok == false
    Deadline() (deadline time.Time, ok bool)
    // 绑定当前context的任务取消时返回一个关闭的channel
    // 当前context不会被取消时返回nil
    Done() <-chan struct{}
    // Done返回的channel没有关闭时返回nil
    // Done返回的channel已经关闭时返回非空的值表示任务结束的原因
    // context取消后Err返回Canceled
    // context超时后Err返回DeadlineExceeded
    Err() error
	// Value返回context存储的键值对中key对应的值,没有该key时返回nil
    Value(key interface{}) interface{}
}

emptyCtx

 想象一棵树,树的每个节点可能有一组键值对。如果当前节点上无法找到key对应的值,就会向上去父节点找,直到根节点。
emptyCtx真实类型是int,实现了context接口,没有超时时间,不能取消,不能存储任何额外信息,所以emptyCtx用来作为context树的根节点。
一般使用由emptyCtx实例化的两个变量,分别可以通过调用Background和TODO方法得到,但这两个context在实现上是一样的。
Background和TODO用于不同场景下:
Background通常被用于主函数、初始化以及测试中,作为一个顶层context,一般创建的context都是基于Background;TODO是在不确定使用什么context时才会使用。

valueCtx

type valueCtx struct {
    Context
    key, val interface{}
}

func (c *valueCtx) Value(key interface{}) interface{} {
    if c.key == key {
        return c.val
    }
    return c.Context.Value(key)
}

valueCtx利用一个Context类型的变量来表示父节点context,所以当前context继承了父context的所有信息;valueCtx类型可以携带一组键值对。如果当前context上不存在key,会沿着context链向上寻找key对应的值,直到根节点。

WithValue向context添加键值对

func WithValue(parent Context, key, val interface{}) Context {
    if key == nil {
        panic("nil key")
    }
    if !reflect.TypeOf(key).Comparable() {
        panic("key is not comparable")
    }
    return &valueCtx{parent, key, val}
}

不是在原context结构体上直接添加键值对,以此context作为父节点,重新创建一个新的valueCtx子节点,将键值对添加在子节点上,由此形成一条context链。

参考资料

深入理解Golang之context