Done():
返回一个channel,可以表示 context 被取消的信号。
当channel被关闭或者到了deadline时,返回一个被关闭的channel。这是一个只读channel。根据golang里相关知识,读取被关闭的channel会读取相应的零值。并且源码里没有地方会向这个 channel 里面塞入值,因此在子协程里读这个 channel,除非被关闭,否则读不出任何东西。也正是利用这一点,子协程从channel里读出了值(零值)后,就可以做一些清理工作,尽快退出。
Deadline():
主要用于设定超时时间的Context上,它的返回值(返回父任务设置的超时时间)用于表示该Context取消的时间点,通过这个时间,就可以判断接下来的操作。比如超时,可以取消操作。
Value():
获取前面设置的key对于的value值
Err():
返回一个错误,表示channel被关闭的原因。比如是被取消,还是超时
emptyCtx是一个不会被取消、没有到期时间、没有值、不会返回错误的context的实现,其主要作为context.Background()和context.TODO()返回这种根context或者不做任何操作的context。如果用父子关系来理解,emptyCtx就是用来创建父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" } var ( background = new(emptyCtx) todo = new(emptyCtx) ) 4.4 cancelCtx结构体cancelCtx struct:
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 }Context:cancelCtx嵌入一个Context接口对象,作为一个匿名字段。这个Context就是父context
mu:保护之后的字段
children:内部通过这个children保存所有可以被取消的context的接口,到后面,如果当前context被取消的时候,只需要调用所有canceler接口的context就可以实现当前调用链的取消
done:取消的信号
err:错误信息
Done() 函数:
func (c *cancelCtx) Done() <-chan struct{} { c.mu.Lock() if c.done == nil { c.done = make(chan struct{}) } d := c.done c.mu.Unlock() return d }函数返回一个只读channel,而且没有地方向这个channel里写数据。所以直接调用这个只读channel会被阻塞。一般通过搭配 select 来使用。一旦关闭,就会立即读出零值。
cancel() 函数:
func (c *cancelCtx) cancel(removeFromParent bool, err error) { if err == nil {// 必须传一个err值,后面判断用 panic("context: internal error: missing cancel error") } c.mu.Lock() if c.err != nil { c.mu.Unlock() return // already canceled 已经被其他协程取消了 } c.err = err // 关闭channel,通知其他协程 if c.done == nil { c.done = closedchan } else { close(c.done) } //遍历它是所有子节点 for child := range c.children { // NOTE: acquiring the child's lock while holding parent's lock. child.cancel(false, err)// 递归地取消所有子节点 } // 将子节点清空 c.children = nil c.mu.Unlock() if removeFromParent { // 从父节点中移除自己 removeChild(c.Context, c) } }这个函数功能就是关闭channel:c.done();
递归取消它的所有子节点;最后从父节点删除自己。
通过关闭channel,将取消信号传递给了它的所有子节点。
goroutine 接收到取消信号的方式就是 select 语句中的 读c.done 被选中
timerCtx struct:
type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. deadline time.Time }