golang context.Context包阅读

324 阅读2分钟

content.Context 接口

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

1、WithCancel: 根据父parent Context构建一个子context

// 根据父parent context构建子context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	c := newCancelCtx(parent)  // 构建子context结构
	propagateCancel(parent, &c)   // 构建父子context的关系
	return &c, func() { c.cancel(true, Canceled) }
}
// propagateCancel arranges for child to be canceled when parent is.
// 父context在cancel的时候,通知子context进行cancel
func propagateCancel(parent Context, child canceler) {
	done := parent.Done() // 判断父context是否可以cancel
	if done == nil {
		return // parent is never canceled
	}

	select {
	case <-done:
		// parent is already canceled
                // 如果parent context已经canceld, 则cancel child context
		child.cancel(false, parent.Err())
		return
	default:
	}
        
        // 下面会详细阐述
        // 如果parent context 是否是cancelCtx
	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{}{} 
                        // 将child context绑定到parent context 的children map中
		}
		p.mu.Unlock()
	} else {
                // 如果parent context 不是cancelCtx类型,则启动新的goroutine进行监控
		atomic.AddInt32(&goroutines, +1)
		go func() {
			select {
			case <-parent.Done():
				child.cancel(false, parent.Err())
			case <-child.Done():
			}
		}()
	}
}
// 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) {
        // 非对外接口,内部调用确保了err的传值
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	c.mu.Lock()
	if c.err != nil {
                // 如果c.err != nil, 代表着对应的context已经被canceled
		c.mu.Unlock()
		return // already canceled
	}
	c.err = err
        // 懒加载,确保c.done的赋值,closedchan是包内可重复使用的已经关闭的chan
	if c.done == nil {
		c.done = closedchan
	} else {
		close(c.done)
	}
        // cancel对应的children context
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err)
	}
        // 将对应的children赋空
	c.children = nil
	c.mu.Unlock()

	if removeFromParent {
		removeChild(c.Context, c)
	}
}

重点:parentCancelCtx

func parentCancelCtx(parent Context) (*cancelCtx, bool) {
        // 如果done为nil,说明parent context不支持 cancel,那么就不可能是 cancelCtx
	// 如果done为可复用的closedchan,说明 parent context 已经被canceled
	done := parent.Done()
	if done == closedchan || done == nil {
		return nil, false
	}
       
        // 如果parent不是context 原生的 cancelCtx,则返回false
	p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
	if !ok {
		return nil, false
	}

	p.mu.Lock()
        
	ok = p.done == done
        // p.done == done,表明当前parent实现了cancelCtx类型
        // p.done != done,表明当前parent未实现cancelCtx类型,有可能是parent.parent context的类型是cancelCtx,因此不能保证parent context可以控制child context, 只能单独监听parent.Done()
	
        p.mu.Unlock()
	if !ok {
		return nil, false
	}
	return p, true
}

其他的WithValue(), WithDeadline(), WithTimeOut()就不做阐述,类似上面的流程