深入理解golang:Context (2)

Done():
返回一个channel,可以表示 context 被取消的信号。
当channel被关闭或者到了deadline时,返回一个被关闭的channel。这是一个只读channel。根据golang里相关知识,读取被关闭的channel会读取相应的零值。并且源码里没有地方会向这个 channel 里面塞入值,因此在子协程里读这个 channel,除非被关闭,否则读不出任何东西。也正是利用这一点,子协程从channel里读出了值(零值)后,就可以做一些清理工作,尽快退出。

Deadline():
主要用于设定超时时间的Context上,它的返回值(返回父任务设置的超时时间)用于表示该Context取消的时间点,通过这个时间,就可以判断接下来的操作。比如超时,可以取消操作。

Value():
获取前面设置的key对于的value值

Err():
返回一个错误,表示channel被关闭的原因。比如是被取消,还是超时

4.3 emptyCtx结构体

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 被选中

4.5 timerCtx 结构体

timerCtx struct:

type timerCtx struct {     cancelCtx     timer *time.Timer // Under cancelCtx.mu.     deadline time.Time }

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

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