Context就相当于一个树状结构
最后请回答一下这个问题:context包中的方法是线程安全吗?
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
type valueCtx struct { Context key, val interface{} } type cancelCtx struct { Context mu sync.Mutex // protects following fields done 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 } type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time }
package context ... var ( background = new(emptyCtx) todo = new(emptyCtx) )
分别通过以下两个方法返回
其中Background()方法是返回初始化时自动实例化的background对象,TODO方法跟Background()相同
context.Background()
context.TODO()
func Background() Context { return background } TODO方法跟Background()相同 func TODO() Context { return todo }
emptyCtx是一个自定义的类型,底层类型为int,实现了Context接口的四个方法,并都返回空值或初始值
type emptyCtx int func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() <-chan struct{} { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil } func (e *emptyCtx) String() string { switch e { case background: return "context.Background" case todo: return "context.TODO" } return "unknown empty Context" }
创建具有dealline的Context WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
创建具有取消方法的Context WithCancel(parent Context) (ctx Context, cancel CancelFunc)
创建具有超时的Context WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
创建具有可保存键值的Context WithValue(parent Context, key, val interface{}) Context
type valueCtx struct { Context //相当于父节点 key, val interface{} } func WithValue(parent Context, key, val interface{}) 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} } //重写了Context接口中的 Value(key interface{})方法 func (c *valueCtx) Value(key interface{}) interface{} { //先从自己节点中的键对值去寻找 if c.key == key { return c.val } //找不到就往上递归,依次寻找绑定的父节点的value return c.Context.Value(key) }
写一个小demo验证一下
func main() { ctx := context.WithValue(context.Background(), "xiaofu", "test") ctx1 := context.WithValue(ctx, "xiaofu1", "test1") fmt.Println(ctx1.Value("xiaofu1")) fmt.Println(ctx1.Value("xiaofu")) } //输出 test1 test <nil> //说明是会往上递归,直到找到background的根节点
查看以下代码,都会发现每次新建Context,都会绑定父节点的Context
type cancelCtx struct { Context //父节点 mu sync.Mutex // 锁 done chan struct{} // channel,用于关闭 children map[canceler]struct{} // 用于储存子节点中的cancelCtx err error // set to non-nil by the first cancel call } //重写了Value方法,当key为cancelCtxKey时,返回当前的cancelCtx,否则不断向上递归寻找cancelCtx func (c *cancelCtx) Value(key interface{}) interface{} { if key == &cancelCtxKey { return c } return c.Context.Value(key) } //重写了Done方法 func (c *cancelCtx) Done() <-chan struct{} { //上锁 c.mu.Lock() //初始化done的channel,根节点的Done()方法返回的是nil if c.done == nil { c.done = make(chan struct{}) } d := c.done //解锁 c.mu.Unlock() return d } func (c *cancelCtx) Err() error { c.mu.Lock() err := c.err c.mu.Unlock() return err } var closedchan = make(chan struct{}) //用于控制取消操作的接口,其中因为cancelCtx实现了cancel方法和Done()方法,所以默认实现该接口 type canceler interface { cancel(removeFromParent bool, err error) Done() <-chan struct{} } func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { if parent == nil { //检查父节点 panic("cannot create context from nil parent") } c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } } //创建cancelCtx结构体,并绑定父节点 func newCancelCtx(parent Context) cancelCtx { return cancelCtx{Context: parent} }
type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time } func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if parent == nil { panic("cannot create context from nil parent") } if cur, ok := parent.Deadline(); ok && cur.Before(d) { // The current deadline is already sooner than the new one. return WithCancel(parent) } //把当前的父节点用一个中间对象cancelCtx做转换,同时绑定到timeCtx中 //约等于 temp := cancelCtx{Context: parent} // c := &timerCtx{ // cancelCtx: temp, // deadline: d, // } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } propagateCancel(parent, c) dur := time.Until(d) if dur <= 0 { c.cancel(true, DeadlineExceeded) // deadline has already passed return c, func() { c.cancel(false, Canceled) } } c.mu.Lock() defer c.mu.Unlock() if c.err == nil { c.timer = time.AfterFunc(dur, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) } }
//相当于在当前时间dealline的基础上,往后延迟一段时间,所以可以调用WithDeadline方法 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }
因为每次执行WithXXX方法,都会新建一个context对象,并且把父对象进行绑定。
见demo
func main() { ctx := context.WithValue(context.Background(), "xiaofu", "test") go func() { _ = context.WithValue(ctx, "xiaofu", "test1") }() go func() { _ = context.WithValue(ctx, "xiaofu", "test2") }() fmt.Println(ctx.Value("xiaofu")) fmt.Println(ctx.Value("xiaofu")) time.Sleep(3 * time.Second) } //输出 //test //test