golang context源码

解析

context结构

Deadline:返回 context 的过期时间;

Done:返回 context 中的 channel;

Err:返回错误;

Value:返回 context 中的对应 key 的值.

cpp 复制代码
type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key any) any
}
cpp 复制代码
//Context包含截止日期、取消信号和其他值
//API边界。
//Context的方法可能会被多个goroutine同时调用。
type Context interface {
	//Deadline返回代表此上下文完成工作的时间
	//应该取消。当没有截止日期时,截止日期返回ok==false
	//集。连续调用Deadline返回相同的结果。
	Deadline() (deadline time.Time, ok bool)

	//Done返回一个通道,当代表此完成工作时,该通道将关闭
	//上下文应该被取消。如果此上下文可以,Done可能会返回nil
	//永远不会被取消。连续调用Done返回相同的值。
	//Done通道的关闭可以异步发生,
	//在取消功能返回后。
	//
	//WithCancel安排在调用取消时关闭Done;
	//WithDeadline安排在截止日期前关闭"完成"
	//到期;WithTimeout安排在超时时关闭"完成"
	//过去了。
	//
	//Done用于选择语句:
	//
	Stream使用DoSomething生成值并将其发送到out
	直到DoSomething返回错误或ctx。完成已关闭。
	//func流(ctx context.context,out chan<-值)错误{
	//for{
	//v,err:=DoSomething(ctx)
	//如果错误!=无{
	//返回错误
	//  		}
	//选择{
	//病例<-ctx。Done():
	//返回ctx。错误()
	//case out<-v:
	//  		}
	//  	}
	//  }
	//
	//请参阅https://blog.golang.org/pipelines有关如何使用的更多示例
	//a取消的完成频道。
	Done() <-chan struct{}

	//如果Done尚未关闭,Err将返回nil。
	//如果Done关闭,Err返回一个非nil错误,解释原因:
	//如果上下文被取消,则取消
	//或超过上下文的截止日期。
	//在Err返回非nil错误后,对Err的连续调用将返回相同的错误。
	Err() error

	//Value返回与key或nil的上下文关联的值
	//如果没有值与键相关联。连续调用Value
	//相同的键返回相同的结果。
	//
	//仅对传输的请求范围数据使用上下文值
	//进程和API边界,不用于将可选参数传递到
	//功能。
	//
	//键标识上下文中的特定值。希望的功能
	//在Context中存储值通常会在全局中分配一个键
	//然后,变量将该键用作上下文的参数。WithValue和
	//背景。价值。密钥可以是任何支持相等的类型;
	//包应将密钥定义为未导出类型,以避免
	//碰撞。
	//
	//定义Context键的包应提供类型安全的访问器
	//对于使用该键存储的值:
	//
	包用户定义了一个存储在上下文中的用户类型。
	//包用户
	//
	//导入"上下文"
	//
	用户是存储在上下文中的值的类型。
	//type用户结构{...}
	//
	key是此包中定义的密钥的未导出类型。
	这可以防止与其他包中定义的键发生冲突。
	//类型键int
	//
	userKey是用户的密钥。上下文中的用户值。它是
	未报告;客户端使用用户。NewContext和用户。从上下文
	而不是直接使用此键。
	//var用户密钥
	//
	NewContext返回一个携带值u的新Context。
	//func-NewContext(ctx-context.context,u*User)上下文。背景{
	//返回上下文。WithValue(ctx、userKey、u)
	// 	}
	//
	FromContext返回存储在ctx中的User值(如果有的话)。
	//func FromContext(ctx context.context)(*用户,bool){
	//u,ok:=ctx。值(userKey)。(*用户)
	//还你,好吗
	// 	}
	Value(key any) any
}

自定义一些结构

Error

cpp 复制代码
//Canceled是取消上下文时[Context.Err]返回的错误。
var Canceled = errors.New("context canceled")

//DeadlineExceded是[Context.Err]在上下文发生错误时返回的错误
// deadline passes.
var DeadlineExceeded error = deadlineExceededError{}

type deadlineExceededError struct{}

func (deadlineExceededError) Error() string   { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool   { return true }
func (deadlineExceededError) Temporary() bool { return true }
cpp 复制代码
//当满足以下条件时,stopCtx用作cancelCtx的父上下文
//AfterFunc已向父级注册。
//它包含用于注销AfterFunc的stop函数。
type stopCtx struct {
	Context
	stop func() bool
}
cpp 复制代码
//goroutines统计曾经创建的goroutines的数量;用于测试。
var goroutines atomic.Int32
cpp 复制代码
//&cancelCtxKey是cancelCtx返回自己的密钥。
var cancelCtxKey int

//parentCancelCtx返回父级的基础*cancelCtx。
//它通过查找父母来做到这一点。要查找的值(&cancelCtxKey)
//最里面的封闭*cancelCtx,然后检查是否
//父母。Done()与*cancelCtx匹配。(如果没有,*cancelCtx
//已被封装在自定义实现中,提供
//不同的完成渠道,在这种情况下,我们不应该绕过它。)
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
	}
	pdone, _ := p.done.Load().(chan struct{})
	if pdone != done {
		return nil, false
	}
	return p, true
}

//removeChild从其父级删除上下文。
func removeChild(parent Context, child canceler) {
	if s, ok := parent.(stopCtx); ok {
		s.stop()
		return
	}
	p, ok := parentCancelCtx(parent)
	if !ok {
		return
	}
	p.mu.Lock()
	if p.children != nil {
		delete(p.children, child)
	}
	p.mu.Unlock()
}

//canceler是一种可以直接取消的上下文类型。这个
//实现是*cancelCtx和*timerCtx。
type canceler interface {
	cancel(removeFromParent bool, err, cause error)
	Done() <-chan struct{}
}

//closedchan是一种可重复使用的封闭通道。
var closedchan = make(chan struct{})

func init() {
	close(closedchan)
}

四种Context

emptyCtx

cpp 复制代码
//emptyCtx永远不会被取消,没有值,也没有截止日期。
//它是背景Ctx和todoCtx的共同基础。
type emptyCtx struct{}
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 any) any {
	return nil
}
cpp 复制代码
type backgroundCtx struct{ emptyCtx }

func (backgroundCtx) String() string {
	return "context.Background"
}

type todoCtx struct{ emptyCtx }

func (todoCtx) String() string {
	return "context.TODO"
}

//Background返回一个非nil的空[Context]。它从未被取消,没有
//值,并且没有截止日期。它通常由主功能使用,
//初始化和测试,并作为传入的顶级上下文请求:
func Background() Context {
	return backgroundCtx{}
}

//TODO返回一个非nil的空[Context]。代码应该使用上下文。TODO何时
//不清楚使用哪个上下文,或者它还不可用(因为
//周围函数尚未扩展为接受上下文
//参数)。
func TODO() Context {
	return todoCtx{}
}

cancelCtx

embed 了一个 context 作为其父 context. 可见,cancelCtx 必然为某个 context 的子 context;

内置了一把锁,用以协调并发场景下的资源获取;

done:实际类型为 chan struct{},即用以反映 cancelCtx 生命周期的通道;

children:一个 set,指向 cancelCtx 的所有子 context;

err:记录了当前 cancelCtx 的错误. 必然为某个 context 的子 context;

cpp 复制代码
//cancelCtx可以取消。取消后,它也会取消任何孩子
// that implement canceler.
type cancelCtx struct {
	Context

	mu       sync.Mutex            //保护以下字段
	done     atomic.Value          //chan-struct{},延迟创建,由第一个cancel调用关闭
	children map[canceler]struct{} //在第一次取消呼叫时设置为nil
	err      error                 //在第一次取消调用时设置为非nil
	cause    error                 //在第一次取消调用时设置为非nil
}

func (c *cancelCtx) Value(key any) any {
	if key == &cancelCtxKey {
		return c
	}
	return value(c.Context, key)
}

func (c *cancelCtx) Done() <-chan struct{} {
	d := c.done.Load()
	if d != nil {
		return d.(chan struct{})
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	d = c.done.Load()
	if d == nil {
		d = make(chan struct{})
		c.done.Store(d)
	}
	return d.(chan struct{})
}

func (c *cancelCtx) Err() error {
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()
	return err
}

//propagateCancel安排在父级为时取消子级。
//它设置cancelCtx的父上下文。
func (c *cancelCtx) propagateCancel(parent Context, child canceler) {
	c.Context = parent

	done := parent.Done()
	if done == nil {
		return //家长永远不会被取消
	}

	select {
	case <-done:
//家长已取消
		child.cancel(false, parent.Err(), Cause(parent))
		return
	default:
	}

	if p, ok := parentCancelCtx(parent); ok {
//parent是一个*cancelCtx,或从其派生而来。
		p.mu.Lock()
		if p.err != nil {
//家长已被取消
			child.cancel(false, p.err, p.cause)
		} else {
			if p.children == nil {
				p.children = make(map[canceler]struct{})
			}
			p.children[child] = struct{}{}
		}
		p.mu.Unlock()
		return
	}

	if a, ok := parent.(afterFuncer); ok {
//parent实现了AfterFunc方法。
		c.mu.Lock()
		stop := a.AfterFunc(func() {
			child.cancel(false, parent.Err(), Cause(parent))
		})
		c.Context = stopCtx{
			Context: parent,
			stop:    stop,
		}
		c.mu.Unlock()
		return
	}

	goroutines.Add(1)
	go func() {
		select {
		case <-parent.Done():
			child.cancel(false, parent.Err(), Cause(parent))
		case <-child.Done():
		}
	}()
}

type stringer interface {
	String() string
}

func contextName(c Context) string {
	if s, ok := c.(stringer); ok {
		return s.String()
	}
	return reflectlite.TypeOf(c).String()
}

func (c *cancelCtx) String() string {
	return contextName(c.Context) + ".WithCancel"
}

//cancel关闭c.done,取消c的每个孩子,如果
//removeFromParent为true,从其父级的子级中删除c。
//如果这是第一次取消c,则设置c。
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	if cause == nil {
		cause = err
	}
	c.mu.Lock()
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
	c.err = err
	c.cause = cause
	d, _ := c.done.Load().(chan struct{})
	if d == nil {
		c.done.Store(closedchan)
	} else {
		close(d)
	}
	for child := range c.children {
		//注意:在持有父母锁的同时获取孩子的锁。
		child.cancel(false, err, cause)
	}
	c.children = nil
	c.mu.Unlock()

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


//WithoutCancel返回父级的副本,当父级被取消时,该副本不会被取消。
//返回的上下文不返回Deadline或Err,其Done通道为nil。
//对返回的上下文调用[Cause]返回nil。
func WithoutCancel(parent Context) Context {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	return withoutCancelCtx{parent}
}

type withoutCancelCtx struct {
	c Context
}

func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (withoutCancelCtx) Done() <-chan struct{} {
	return nil
}

func (withoutCancelCtx) Err() error {
	return nil
}

func (c withoutCancelCtx) Value(key any) any {
	return value(c, key)
}

func (c withoutCancelCtx) String() string {
	return contextName(c.c) + ".WithoutCancel"
}

//WithDeadline返回父上下文的副本,并调整了截止日期
//如果家长的截止日期已经早于d,
//WithDeadline(parent,d)在语义上等同于parent。回归
//[Context.Done]通道在截止日期到期时关闭,当返回
//cancel函数被调用,或者当父上下文的Done通道为
//关闭,以先发生者为准。
//
//取消此上下文会释放与其关联的资源,因此代码应该
//一旦在此[Context]中运行的操作完成,就调用cancel。
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	return WithDeadlineCause(parent, d, nil)
}

//WithDeadlineCause的行为类似于[WithDeadline],但也设置了
//超过截止日期时返回上下文。返回的[CancelFunc]
//不确定原因。
func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}
	c := &timerCtx{
		deadline: d,
	}
	c.cancelCtx.propagateCancel(parent, c)
	dur := time.Until(d)
	if dur <= 0 {
		c.cancel(true, DeadlineExceeded, cause) // deadline has already passed
		return c, func() { c.cancel(false, Canceled, nil) }
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
		c.timer = time.AfterFunc(dur, func() {
			c.cancel(true, DeadlineExceeded, cause)
		})
	}
	return c, func() { c.cancel(true, Canceled, nil) }
}

timerCtx

timerCtx 在 cancelCtx 基础上又做了一层封装,除了继承 cancelCtx 的能力之外,新增了一个 time.Timer 用于定时终止 context;另外新增了一个 deadline 字段用于字段 timerCtx 的过期时间.

cpp 复制代码
//timerCtx包含计时器和截止日期。它嵌入了cancelCtx
//实现完成和错误。它通过停止计时器来实现取消
//委托取消Ctx.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 contextName(c.cancelCtx.Context) + ".WithDeadline(" +
		c.deadline.String() + " [" +
		time.Until(c.deadline).String() + "])"
}

func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
	c.cancelCtx.cancel(false, err, cause)
	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()
}

// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this [Context] complete:
//
//	func slowOperationWithTimeout(ctx context.Context) (Result, error) {
//		ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
//		defer cancel()  // releases resources if slowOperation completes before timeout elapses
//		return slowOperation(ctx)
//	}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

// WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the
// returned Context when the timeout expires. The returned [CancelFunc] does
// not set the cause.
func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc) {
	return WithDeadlineCause(parent, time.Now().Add(timeout), cause)
}

valueCtx

cpp 复制代码
//WithValue返回父项的副本,其中与键关联的值为
//瓦尔。
//
//仅对传输进程和进程的请求范围数据使用上下文值
//API,不用于向函数传递可选参数。
//
//提供的密钥必须具有可比性,并且不应为同一类型
//string或任何其他内置类型,以避免它们之间的冲突
//使用上下文的包。WithValue的用户应该定义自己的
//按键类型。为了避免在分配时进行分配
//接口{},上下文键通常具有具体类型
//结构{}。或者,导出上下文键变量的静态
//类型应该是指针或接口。
func WithValue(parent Context, key, val any) Context {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	if key == nil {
		panic("nil key")
	}
	if !reflectlite.TypeOf(key).Comparable() {
		panic("key is not comparable")
	}
	return &valueCtx{parent, key, val}
}

//valueCtx包含一个键值对。它为该键实现了Value
//将所有其他调用委托给嵌入式Context。
type valueCtx struct {
	Context
	key, val any
}

//stringify尝试在不使用fmt的情况下对v进行字符串化,因为我们没有
//希望上下文取决于unicode表。这仅用于
//*valueCtx。String()。
func stringify(v any) string {
	switch s := v.(type) {
	case stringer:
		return s.String()
	case string:
		return s
	case nil:
		return "<nil>"
	}
	return reflectlite.TypeOf(v).String()
}

func (c *valueCtx) String() string {
	return contextName(c.Context) + ".WithValue(" +
		stringify(c.key) + ", " +
		stringify(c.val) + ")"
}

func (c *valueCtx) Value(key any) any {
	if c.key == key {
		return c.val
	}
	return value(c.Context, key)
}

func value(c Context, key any) any {
	for {
		switch ctx := c.(type) {
		case *valueCtx:
			if key == ctx.key {
				return ctx.val
			}
			c = ctx.Context
		case *cancelCtx:
			if key == &cancelCtxKey {
				return c
			}
			c = ctx.Context
		case withoutCancelCtx:
			if key == &cancelCtxKey {
				//这实现了Cause(ctx)==nil
//当使用WithoutCancel创建ctx时。
				return nil
			}
			c = ctx.c
		case *timerCtx:
			if key == &cancelCtxKey {
				return &ctx.cancelCtx
			}
			c = ctx.Context
		case backgroundCtx, todoCtx:
			return nil
		default:
			return c.Value(key)
		}
	}
}

其他

函数

CancelFunc

cpp 复制代码
//CancelFunc告诉一个操作放弃它的工作。
//CancelFunc不会等待工作停止。
//CancelFunc可以被多个goroutines同时调用。
//在第一次调用之后,对CancelFunc的后续调用什么也不做。
type CancelFunc func()

//WithCancel返回一个父级的副本,其中包含一个新的"完成"频道。回归
//当调用返回的cancel函数时,上下文的Done通道关闭
//或者当父上下文的"完成"通道关闭时,以先发生者为准。
//
//取消此上下文会释放与其关联的资源,因此代码应该
//一旦在此[Context]中运行的操作完成,就调用cancel。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := withCancel(parent)
	return c, func() { c.cancel(true, Canceled, nil) }
}

CancelCauseFunc

cpp 复制代码
//CancelCauseFunc的行为类似于[CancelFunc],但还会设置取消原因。
//可以通过在取消的上下文或上调用[cause]来检索此原因
//其衍生的任何上下文。
//
//如果上下文已被取消,CancelCauseFunc不会设置原因。
//例如,如果childContext派生自parentContext:
//-如果parentContext在childContext被cause 2取消之前被cause 1取消,
//那么原因(parentContext)==原因(childContext)==Cause 1
//-如果childContext在parentContext被cause 1取消之前被cause 2取消,
//那么原因(parentContext)==原因1和原因(childContext)==s原因2
type CancelCauseFunc func(cause error)

//WithCancelCause的行为类似于[WithCancel],但返回的是[CancelCauseFunc]而不是[CancelFunc]。
//调用带有非nil错误("cause")的cancel会在ctx中记录该错误;
//然后可以使用Cause(ctx)检索它。
//使用nil调用cancel会将原因设置为Canceled。
//
//示例用法:
//
//ctx,取消:=上下文。取消原因(父)
//取消(myError)
//ctx。Err()//返回上下文。被取消
//背景。Cause(ctx)//返回myError
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) {
	c := withCancel(parent)
	return c, func(cause error) { c.cancel(true, Canceled, cause) }
}

func withCancel(parent Context) *cancelCtx {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	c := &cancelCtx{}
	c.propagateCancel(parent, c)
	return c
}

//Cause返回一个非nil错误,解释为什么c被取消。
//c或其父项之一的首次取消确定了原因。
//如果取消是通过调用CancelCauseFunc(err)发生的,
//则[Cause]返回err。
//否则,Cause(c)返回与c.Err()相同的值。
//如果c尚未取消,则Cause返回nil。
func Cause(c Context) error {
	if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok {
		cc.mu.Lock()
		defer cc.mu.Unlock()
		return cc.cause
	}
	//没有cancelCtxKey值,所以我们知道c是
	//不是WithCancelCause创建的某些上下文的后代。
	//因此,没有具体的返回原因。
	//如果这不是标准上下文类型之一,
	//即使没有原因,它也可能仍然有错误。
	return c.Err()
}

AfterFunc

cpp 复制代码
//AfterFunc安排在ctx完成后在自己的goroutine中调用f
//(已取消或超时)。
//如果ctx已经完成,AfterFunc会立即在自己的goroutine中调用f。
//
//在一个上下文中对AfterFunc的多个调用独立操作;
//一个不能取代另一个。
//
//调用返回的stop函数停止ctx与f的关联。
//如果调用停止f的运行,则返回true。
//如果stop返回false,
//要么上下文已经完成,f已经在自己的goroutine中启动;
//或者f已经停止了。
//stop函数在返回之前不会等待f完成。
//如果呼叫者需要知道f是否完成,
//它必须与f显式协调。
//
//如果ctx有一个"AfterFunction(func)func(bool)"方法,
//AfterFunc将使用它来安排通话。
func AfterFunc(ctx Context, f func()) (stop func() bool) {
	a := &afterFuncCtx{
		f: f,
	}
	a.cancelCtx.propagateCancel(ctx, a)
	return func() bool {
		stopped := false
		a.once.Do(func() {
			stopped = true
		})
		if stopped {
			a.cancel(true, Canceled, nil)
		}
		return stopped
	}
}

afterFuncer

cpp 复制代码
type afterFuncer interface {
	AfterFunc(func()) func() bool
}

type afterFuncCtx struct {
	cancelCtx
	once sync.Once //要么开始运行f,要么停止运行f
	f    func()
}

func (a *afterFuncCtx) cancel(removeFromParent bool, err, cause error) {
	a.cancelCtx.cancel(false, err, cause)
	if removeFromParent {
		removeChild(a.Context, a)
	}
	a.once.Do(func() {
		go a.f()
	})
}
相关推荐
sweetying16 分钟前
30了,人生按部就班
android·程序员
用户2018792831671 小时前
Binder驱动缓冲区的工作机制答疑
android
真夜1 小时前
关于rngh手势与Slider组件手势与事件冲突解决问题记录
android·javascript·app
用户2018792831671 小时前
浅析Binder通信的三种调用方式
android
用户091 小时前
深入了解 Android 16KB内存页面
android·kotlin
火车叼位2 小时前
Android Studio与命令行Gradle表现不一致问题分析
android
前行的小黑炭4 小时前
【Android】 Context使用不当,存在内存泄漏,语言不生效等等
android·kotlin·app
前行的小黑炭5 小时前
【Android】CoordinatorLayout详解;实现一个交互动画的效果(上滑隐藏,下滑出现);附例子
android·kotlin·app
用户20187928316717 小时前
Android黑夜白天模式切换原理分析
android
芦半山18 小时前
「幽灵调用」背后的真相:一个隐藏多年的Android原生Bug
android