使用场景
在协程之间传递上下文
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链。