go context专题(二)- context设计目的和基本数据结构
来源:互联网 发布:js中用parse判断大小 编辑:程序博客网 时间:2024/05/18 02:13
go context专题(二)- context设计目的和基本数据结构
设计目的
context库的设计目的就是跟踪 goroutine调用树,并在在这些gouroutine调用树中传递通知和元数据。两个目的:
1.退出通知机制 (主要目的)
2.传递元数据(辅助功能)
基本的数据结构
1. 2个接口
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接口
// 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接口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
// 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
// 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
// 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.包装函数
func TODO() Context
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()}
- go context专题(二)- context设计目的和基本数据结构
- go context专题(三)- context 工作机制和代码分析
- go context专题(四)- context 最佳实践和相关争议
- go context专题(一)- go 并发编程基础设施
- Go Context包使用
- Android深入理解Context(二)Activity和Service的Context创建过程
- Flask中上下文栈(context stacks)的目的?
- iOS Context 基础教程 (二)
- Context
- Context
- Context
- context
- Context
- context
- Context
- Context
- Context
- Context
- 一个简单的SpringMVC实例
- html行内元素和块级元素的区别
- 迭代器模式
- 函数防抖和函数节流
- ASP.NET上传文件
- go context专题(二)- context设计目的和基本数据结构
- 乘法表(百度2016实习生真题)-- java
- 逻辑运算符
- Python __slots__ and __dict__
- SQL SERVER数据库升级
- 集合相减A-B
- FCC Spinal Tap Case
- 动态链接的整个过程
- Web Storage