go context专题(二)- context设计目的和基本数据结构

来源:互联网 发布:js中用parse判断大小 编辑:程序博客网 时间:2024/05/18 02:13

go context专题(二)- context设计目的和基本数据结构


设计目的

context库的设计目的就是跟踪 goroutine调用树,并在在这些gouroutine调用树中传递通知和元数据。两个目的:

1.退出通知机制 (主要目的)

2.传递元数据(辅助功能)


基本的数据结构

为了理解context的实现,需要理解下设计规则:context的创建者称为root节点,其一般是一个处理上下文的独立goroutine。root节点负责创建Context的具体对象,并将其传递到其下游调用的goroutine. 下游的goroutine可以继续封装改Context对象(使用装饰者模式)再传递更下游的goroutine.这些下游goroutine的Context 对象实例都要逐层向上注册。这样通过root节点的Context对象就可以遍历整个Context对象树,所以通知也能通知到下游的goroutine.

1.  2个接口

Context接口

是一个基本的接口,所有的Context对象都要实现该接口,context的使用者在在调用接口中都是使用Context最为参数类型。
具体解释如下
#Context接口type Context interface {//如果Context实现了超时控制,该方法返回ok true deadline为超时时间,否则ok 为false    Deadline() (deadline time.Time, ok bool)        //后端被调的goroutine应该监听该方法返回的chan,以便及时的释放资源    Done() <-chan struct{}        //Done返回的chan收到通知的时候,才可以访问Err()获知因为什么原因被取消    Err() error        //可以访问上游goroutine传递给下游goroutine的值    Value(key interface{}) interface{}}

canceler接口

是一个扩展接口,其规定了取消通知的Context 具体类型需要实现的接口。context包中:*cancelCtx and *timerCtx 都实现了该接口。

 // A canceler is a context type that can be canceled directly. The                                     // implementations are *cancelCtx and *timerCtx. //一个Context 对象如果实现了canceler接口,可以被取消                                                       type canceler interface {                        //cancel 是主调goroutine负责调用     cancel(removeFromParent bool, err error)  //Done方法返回的chan需要后端goroutine来监听,并及时退出                                                              Done() <-chan struct{}                                                                             }


2.  empty  Context结构

emptyCtx 实现了Context接口,但是不具备任何功能,因为其所有的方法都是空实现。其存在的目的是为Context的对象提供一个root根。
因为context的使用思路就是不停的调用context包提供的包装函数创建具有特殊功能的Context实例,使用emptyCtx的实例最为第一层包装。
#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"                                                                    } 

package 定义了两个全局变量和封装函数,返回两个emptyCtx实例对象,通常用它们在第一次调用包装函数时传递进去。
var (    background = new(emptyCtx)    todo       = new(emptyCtx))// Background returns a non-nil, empty Context. It is never canceled, has no// values, and has no deadline. It is typically used by the main function,// initialization, and tests, and as the top-level Context for incoming// requests.func Background() Context {    return background}// TODO returns a non-nil, empty Context. Code should use context.TODO when// it's unclear which Context to use or it is not yet available (because the// surrounding function has not yet been extended to accept a Context// parameter). TODO is recognized by static analysis tools that determine// whether Contexts are propagated correctly in a program.func TODO() Context {    return todo}


3.Context的具体实现类型

cancelCtx

cancelCtx 是一个实现了Context接口的具体类型,其同时实现Context和conceler接口,实现退出通知。注意其退出通知机制不但通知自己,同时也通知其children节点。
// A cancelCtx can be canceled. When canceled, it also cancels any children                               // that implement canceler.                                                                               type cancelCtx struct {                                                                                       Context                                                                                                                                                                                                             done chan struct{} // closed by the first cancel call.                                                                                                                                                              mu       sync.Mutex                                                                                       children map[canceler]bool // set to nil by the first cancel call                                         err      error             // set to non-nil by the first cancel call                                 }                                                                                                                                                                                                                   func (c *cancelCtx) Done() <-chan struct{} {                                                                  return c.done                                                                                         }                                                                                                                                                                                                                   func (c *cancelCtx) Err() error {                                                                             c.mu.Lock()                                                                                               defer c.mu.Unlock()                                                                                       return c.err                                                                                          }                                                                                                                                                                                                                   func (c *cancelCtx) String() string {                                                                         return fmt.Sprintf("%v.WithCancel", c.Context)                                                        }                                                                                                         // cancel closes c.done, cancels each of c's children, and, if// removeFromParent is true, removes c from its parent's children.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        //显示通知自己    close(c.done)//循环调用children的cancel函数,由于parent已经cancel,所以此时child调用cancel传入的是false     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)    }}



timerCtx

timerCtx 是一个实现了Context接口的具体类型,其内部封装了cancelCtx类型实例,同时也有个deadline变量,用来实现定时退出通知。
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to// implement Done and Err. It implements cancel by stopping its timer then// delegating to cancelCtx.cancel.type timerCtx struct {    cancelCtx    timer *time.Timer // Under cancelCtx.mu.                                                                                                              deadline time.Time                                                                                    }                                                                                                                                                                                                                   func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {                                                 return c.deadline, true                                                                               }                                                                                                                                                                                                                   func (c *timerCtx) String() string {                                                                          return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))}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()}



valueCtx

valueCtx是一个实现了Context接口的具体类型,其内部封装了Context接口类型,同时也封装了一个k/v的存储变量,其是一个实现了数据传递
Context具体实现类型。
// A valueCtx carries a key-value pair. It implements Value for that key and                              // delegates all other calls to the embedded Context.                                                     type valueCtx struct {                                                                                        Context                                                                                                   key, val interface{}                                                                                  }                                                                                                                                                                                                                   func (c *valueCtx) String() string {                                                                          return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)                                 }                                                                                                                                                                                                                   func (c *valueCtx) Value(key interface{}) interface{} {                                                       if c.key == key {                                                                                             return c.val                                                                                          }                                                                                                         return c.Context.Value(key)                                                                           } 


4.包装函数

#下面这两个函数是在构成实例的初期构造取消树的根节点时使用Context的对象,为后续的With包装函数的调用创建一个输入参数parent.
func Background() Context
func TODO() Context

创建一个带有退出通知的Context具体对象,其实是创建一个cancelCtx的类型实例
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

创建一个带有超时通知的Context具体对象,其实是创建一个timerCtx的类型实例,其是对WithDeadline的简单封装
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

创建一个带有超时通知的Context具体对象,其实是创建一个timerCtx的类型实例
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

创建一个带有数据的Context具体对象,其实是创建一个valueCtx的类型实例
func WithValue(parent Context, key, val interface{}) Context


这些函数都有个共同的特点有个parent参数,其实这个就是实现Context通知树的必备条件。在goroutine的调用链 Context的实例被逐层的传递,没个层次又可以在传进来的Context实例上再封装自己的需求,但是这整个调用树需要有个结构来维护。其实这个维护过程就是在这些包装函数内部完成。


5.辅助函数

4中的上述的With开头的构造函数是给外部程序使用的开放函数,我们一直讨论的Context具体对象的链条关系的维护,实际是在With函数的内部进行维护的。现在来分析一下With函数内部说使用的通用函数。

func propagateCancel(parent Context, child canceler)其有如下几个功能:

1.判断parent的method  Done返回是否是nil,如果是,说明parent不是一个可取消的Context对象,也就无所谓取消构造树,那说明child就是取消构造树的根

2.判断parent的method  Done返回不是nil,向上回溯自己的祖先是否是cancelCtx类型实例,如果是的话就将chiild的子节点注册维护到那课关系树里面。

3.如果向上回溯自己的祖先不是cancelCtx类型实例,那说明整个链条的取消树不是连续的,有几颗取消树,所以,此时只需监听parent和自己的取消信号即可。

// propagateCancel arranges for child to be canceled when parent is.                                      func propagateCancel(parent Context, child canceler) {                                                        if parent.Done() == nil {                                                                                     return // parent is never canceled                                                                    }                                                                                                         if p, ok := parentCancelCtx(parent); ok {                                                                     p.mu.Lock()                                                                                               if p.err != nil {                                                                                             // parent has already been canceled                                                                       child.cancel(false, p.err)                                                                            } else {                                                                                                      if p.children == nil {                                                                                        p.children = make(map[canceler]bool)                                                                  }                                                                                                         p.children[child] = true                                                                              }                                                                                                         p.mu.Unlock()                                                                                         } else {                                                                                                      go func() {                                                                                                   select {                                                                                                  case <-parent.Done():                                                                                         child.cancel(false, parent.Err())                                                                     case <-child.Done():                                                                                      }                                                                                                     }()                                                                                                   }                                                                                                     }

func parentCancelCtx(parent Context) (*cancelCtx, bool)

判断parent 中是否封装有*cancelCtx的字段或者接口里面存放的底层类型是*cancelCtx类型。

// parentCancelCtx follows a chain of parent references until it finds a// *cancelCtx. This function understands how each of the concrete types in this// package represents its parent.func parentCancelCtx(parent Context) (*cancelCtx, bool) {    for {        switch c := parent.(type) {        case *cancelCtx:            return c, true        case *timerCtx:            return &c.cancelCtx, true        case *valueCtx:            parent = c.Context        default:            return nil, false        }    }}

func removeChild(parent Context, child canceler)

如果parent封装有*cancelCtx的字段或者接口里面存放的底层类型是*cancelCtx类型,这将其构造树上的child节点删除

// removeChild removes a context from its parent.func removeChild(parent Context, child canceler) {    p, ok := parentCancelCtx(parent)    if !ok {        return    }       p.mu.Lock()    if p.children != nil {        delete(p.children, child)    }    p.mu.Unlock()}









原创粉丝点击