九. Go并发编程--context.Context (3)

接下来我们来看最重要的这个 cancel 方法,cancel 接收两个参数,removeFromParent 用于确认是不是把自己从 parent context 中移除,err 是 ctx.Err() 最后返回的错误信息

func (c *cancelCtx) cancel(removeFromParent bool, err error) { if err == nil { panic("context: internal error: missing cancel error") } // 互斥锁用来保证并发安全 c.mu.Lock() if c.err != nil { c.mu.Unlock() return // already canceled } c.err = err // 由于 cancel context 的 done 是懒加载的,所以有可能存在还没有初始化的情况 if c.done == nil { c.done = closedchan } else { close(c.done) } // 循环的将所有的子 context 取消掉 for child := range c.children { // NOTE: acquiring the child's lock while holding parent's lock. child.cancel(false, err) } // 将所有的子 context 和当前 context 关系解除 c.children = nil c.mu.Unlock() // 如果需要将当前 context 从 parent context 移除,就移除掉 if removeFromParent { removeChild(c.Context, c) } } 2.2.4 WithTimeout

WithTimeout 其实就是调用了 WithDeadline 然后再传入的参数上用当前时间加上了 timeout 的时间

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) }

再来看一下实现超时的 timerCtx,WithDeadline 我们放到后面一点点

type timerCtx struct { cancelCtx // 这里复用了 cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time // 这里保存了快到期的时间 }

Deadline() 就是返回了结构体中保存的过期时间

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true }

cancel 其实就是复用了 cancelCtx 中的取消方法,唯一区别的地方就是在后面加上了对 timer 的判断,如果 timer 没有结束主动结束 timer

func (c *timerCtx) cancel(removeFromParent bool, err error) { c.cancelCtx.cancel(false, err) if removeFromParent { // Remove this timerCtx from its parent cancelCtx's children. removeChild(c.cancelCtx.Context, c) } c.mu.Lock() if c.timer != nil { c.timer.Stop() c.timer = nil } c.mu.Unlock() }

timerCtx 并没有重新实现 Done() 和 Value 方法,直接复用了 cancelCtx 的相关方法

2.2.5 WithDeadline

源码如下

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if parent == nil { panic("cannot create context from nil parent") } // 会先判断 parent context 的过期时间,如果过期时间比当前传入的时间要早的话,就没有必要再设置过期时间了 // 只需要返回 WithCancel 就可以了,因为在 parent 过期的时候,子 context 也会被取消掉 if cur, ok := parent.Deadline(); ok && cur.Before(d) { // The current deadline is already sooner than the new one. return WithCancel(parent) } // 构造相关结构体 c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } // 和 WithCancel 中的逻辑相同,构建上下文关系 propagateCancel(parent, c) // 判断传入的时间是不是已经过期,如果已经过期了就 cancel 掉然后再返回 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() // 这里是超时取消的逻辑,启动 timer 时间到了之后就会调用取消方法 if c.err == nil { c.timer = time.AfterFunc(dur, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) } }

可以发现超时控制其实就是在复用 cancelCtx 的基础上加上了一个 timer 来做定时取消

2.2.6 WithValue

主要就是校验了一下 Key 是不是可比较的,然后构造出一个 valueCtx 的结构

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} }

valueCtx 主要就是嵌入了 parent context 然后附加了一个 key val

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgzjys.html