Go语言之Context

Go语言之Context的源码解析

Posted by Lerko on July 18, 2020

Go语言之Context

两个重要的接口


type Context interface {
	//Deadline返回绑定当前context的任务被取消的截止时间;如果没有设定期限,将返回ok == false
	Deadline() (deadline time.Time, ok bool)

    //这个函数返回一个chan用于通知对应的goroutine进行相应的处理,如果chan有值表示的是context已经结束。
	Done() <-chan struct{}

	//Err 如果Done返回的channel没有关闭,将返回nil;如果Done返回的channel已经关闭,将返回非空的值表示任务结束的原因。如果是context被取消,Err将返回Canceled;如果是context超时,Err将返回DeadlineExceeded。
	Err() error

    //Value 返回context存储的键值对中当前key对应的值,如果没有对应的key,则返回nil。
	Value(key interface{}) interface{}
}


//如果是实现了canceler就是代表这个是一个可以取消的context
type canceler interface {
    //调用cancel去结束context
    cancel(removeFromParent bool, err error)
    //和context一致
	Done() <-chan struct{}
}

这边我们就大致知道了两种context

类型 相关结构体 相关创建方法
可取消(Contenxt&&canceler) cancelCtx,timerCtx WithDeadline,WithTimeout,WithCancel
无法取消(Context) valueCtx WithValue

可取消的Context的取消流程

以cancelCtx为例

type cancelCtx struct {
	Context

	mu       sync.Mutex            //保护以下字段
	done     chan struct{}         //延迟创建,先取消后关闭chan
	children map[canceler]struct{} 
	err      error                 
}


func (c *cancelCtx) cancel(removeFromParent bool, err error) {
    //判断是否有错误
	if err == nil {
		panic("context: internal error: missing cancel error")
    }
    //context加锁
	c.mu.Lock()
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
    c.err = err
    //判断当前done是否为空,为空的话赋值一个空chan
	if c.done == nil {
		c.done = closedchan
	} else {
		close(c.done)
    }
    //停止所有的子context
	for child := range c.children {
		// NOTE: 持有父母的锁的同时获得孩子的锁.
		child.cancel(false, err)
	}
	c.children = nil
	c.mu.Unlock()

    //从children中吧对应的context移除
	if removeFromParent {
		removeChild(c.Context, c)
	}
}