美文网首页
Golang源码分析(一) Context

Golang源码分析(一) Context

作者: zzz1t1 | 来源:发表于2022-05-18 21:36 被阅读0次

    context是go应用开发常用的并发控制技术,它与WaitGroup最大的不同点是context对于派生goroutine有更强的控制力,它可以控制多级的goroutine。

    context翻译成中文是”上下文”,即它可以控制一组呈树状结构的goroutine,每个goroutine拥有相同的上下文。

    主要有4个struct体,都继承于有4个方法的Context接口,4个方法分别是Done,Err,DeadLine,Value。

    四个struct体 分别是emptyCtx、cancelCtx、timerCtx、valueCtx,而timerCtx中复用了cancelCtx的Context方法,这也是go常用复用代码手段。

    image.png
    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
    }
    

    正是使用了 sync.Mutex ,所以context是线程安全的。且继承并实现了Context接口的四个方法。先来看下cancel

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

    这里主要实现的是关闭自己和后代,删除存储在children。

    func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
        if parent == nil {
            panic("cannot create context from nil parent")
        }
        c := newCancelCtx(parent)
        propagateCancel(parent, &c)
        return &c, func() { c.cancel(true, Canceled) }
    }
    

    首先初始化了cancelctx,把自己追加到上一级的children里,并返回当前cancelctx和cancel。

    func propagateCancel(parent Context, child canceler) {
        done := parent.Done()
        if done == nil {
            return // parent is never canceled
        }
    
        select {
        case <-done:
            // parent is already canceled
            child.cancel(false, parent.Err())
            return
        default:
        }
    
        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]struct{})
                }
                p.children[child] = struct{}{}
            }
            p.mu.Unlock()
        } else {
            atomic.AddInt32(&goroutines, +1)
            go func() {
                select {
                case <-parent.Done():
                    child.cancel(false, parent.Err())
                case <-child.Done():
                }
            }()
        }
    }
    

    propagateCancel 实现了
    1.如果父级ctx已经关闭了,初始化当前子ctx的cancel
    2.将实例化过子ctx加入父级的ctx的children 中
    3.通过原子操作实现初始化当前子ctx的cancel

    func parentCancelCtx(parent Context) (*cancelCtx, bool) {
        done := parent.Done()
        if done == closedchan || done == nil {
            return nil, false
        }
        p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
        if !ok {
            return nil, false
        }
        p.mu.Lock()
        ok = p.done == done
        p.mu.Unlock()
        if !ok {
            return nil, false
        }
        return p, true
    }
    

    parentCancelCtx返回子ctx实例和是否成功将子ctx加入children

    总结
    1.Context仅仅是一个接口定义,根据实现的不同,可以衍生出不同的context类型;
    2.cancelCtx实现了Context接口,通过WithCancel()创建cancelCtx实例;
    3.timerCtx实现了Context接口,通过WithDeadline()和WithTimeout()创建timerCtx实例;
    4.valueCtx实现了Context接口,通过WithValue()创建valueCtx实例;
    5.三种context实例可互为父节点,从而可以组合成不同的应用形式;

    相关文章

      网友评论

          本文标题:Golang源码分析(一) Context

          本文链接:https://www.haomeiwen.com/subject/mpxzurtx.html