zoukankan      html  css  js  c++  java
  • go context 源码分析

    WithCancel

    func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    	c := newCancelCtx(parent)
    	propagateCancel(parent, &c)
    	return &c, func() { c.cancel(true, Canceled) }
    }
    
    // 包了一层,把 parent 赋值给子 context
    func newCancelCtx(parent Context) cancelCtx {
    	return cancelCtx{Context: parent}
    }
    
    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
    }
    
    func propagateCancel(parent Context, child canceler) {
    	if parent.Done() == nil {
    		return // parent is never canceled
    	}
      // 判断 parent 是否为 cancelCtx,timerCtx 也属于 cancelCtx,cancelCtx 有 child 
    	if p, ok := parentCancelCtx(parent); ok {
    		p.mu.Lock()
    		if p.err != nil {
          // cancelCtx 被 cancel() 的时候,会给err属性赋值
    			// parent has already been canceled
    			child.cancel(false, p.err)
    		} else { // parent 没有被 cancel()
    			if p.children == nil {
    				p.children = make(map[canceler]struct{})
    			}
    			p.children[child] = struct{}{} // 将 child 添加到 parent 中
    		}
    		p.mu.Unlock()
    	} else { // parent 的状态未确定 
    		go func() {
    			select {
    			case <-parent.Done(): // 如果可以从 parent 中获取到值,说明 parent 被 cancel 了
    				child.cancel(false, parent.Err())
    			case <-child.Done():
    			}
    		}()
    	}
    }
    
    // 类型判断
    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
    		}
    	}
    }
    

    分析下 WithCancel 返回的函数

    func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    	c := newCancelCtx(parent)
    	propagateCancel(parent, &c)
    	return &c, func() { c.cancel(true, Canceled) }
    }
    
    func (c *cancelCtx) cancel(removeFromParent bool, err error) {
      // 被 cancel 的 cancelCtx 的 err 属性,必须被赋值
    	if err == nil {
    		panic("context: internal error: missing cancel error")
    	}
    	c.mu.Lock()
    	if c.err != nil {
    		c.mu.Unlock()
    		return // 已经被 cancel
    	}
    	c.err = err
    	if c.done == nil {
    		c.done = closedchan // 注意此处!!!
    	} else {
    		close(c.done) // close 后,select 可以不断获取到默认值
    	}
      // close 掉所有 child
    	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)
    	}
    }
    
    func removeChild(parent Context, child canceler) {
      // 只有 cancelCtx 类型的 context,才有 child
    	p, ok := parentCancelCtx(parent)
    	if !ok {
    		return
    	}
    	p.mu.Lock()
    	if p.children != nil {
    		delete(p.children, child)
    	}
    	p.mu.Unlock()
    }
    
    
    // closedchan is a reusable closed channel.
    // closedchan init() 的时候就被 close 了,所以是可以通过 select 不断获取值的
    var closedchan = make(chan struct{})
    func init() {
    	close(closedchan)
    }
    
  • 相关阅读:
    heartbeat 心跳技术
    国产数据库有哪些?
    Redis(1.9)Redis主从复制
    Redis(1.8)Redis与mysql的数据库同步(缓存穿透与缓存雪崩)
    分布式系统/NoSQL
    Redis(1.7)Redis高可用架构与数据库交互(理论篇)
    Redis(1.6)Redis发布订阅
    Redis(1.5)Redis配置文件(4.0.14)
    (5.3.4)数据库迁移——数据对比(结构、数据类型)
    【基本优化实践】【1.5】如何在线稳定的删除/更新大量数据?
  • 原文地址:https://www.cnblogs.com/zhouj-happy/p/11210047.html
Copyright © 2011-2022 走看看