理解Context

这篇文章介绍的很清楚:深入理解Go Context

这个比较详细,但是层次不好:理解GO CONTEXT机制

关于context的使用场景

context的主要使用场景在于:一个任务在处理的过程中可能会启动很多个协程来进行处理。在这个过程中,如果上游的任务想要取消,下游的任务也应当一起取消。context的任务就来了。

内容介绍

context包的内容可以概括为:1个接口,4个实现,6个方法

context包的内容

接口

context.Context

一个接口是指:context.Context

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

Deadline( )

Deadline会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。

函数签名 Deadline() (deadline time.Time, ok bool)

Deadline 返回的时间 deadline time.Time 代表这个ctx应该被取消的时间。返回的 ok 如果是 false 表示这个context没有设置deadline。连续调用 Deadline 会返回相同的结果。

	// Deadline returns the time when work done on behalf of this context
	// should be canceled. Deadline returns ok==false when no deadline is
	// set. Successive calls to Deadline return the same results.
	Deadline() (deadline time.Time, ok bool)

Done( )

Done方法返回一个信道(channel),当Context被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。

函数签名 Done() <-chan struct{}

Done方法返回一个关闭的通道代表这个context应该被取消,如果这个context永远不会被取消,则Done会返回nil,连续调用会返回相同的值。Done channel的关闭可能是异步的,在cancel函数之后。

  • WithCancel 函数安排当 cancel 被调用的时候关闭Done
  • WithDeadline 安排当 deadline 过期的时候关闭Done
  • WithTimeout 安排当 timeout 过期的时候关闭Done

Done 可以在 select 表达式中使用

	// Done returns a channel that's closed when work done on behalf of this
	// context should be canceled. Done may return nil if this context can
	// never be canceled. Successive calls to Done return the same value.
	// The close of the Done channel may happen asynchronously,
	// after the cancel function returns.
	//
	// WithCancel arranges for Done to be closed when cancel is called;
	// WithDeadline arranges for Done to be closed when the deadline
	// expires; WithTimeout arranges for Done to be closed when the timeout
	// elapses.
	//
	// Done is provided for use in select statements:
	//
	//  // Stream generates values with DoSomething and sends them to out
	//  // until DoSomething returns an error or ctx.Done is closed.
	//  func Stream(ctx context.Context, out chan<- Value) error {
	//  	for {
	//  		v, err := DoSomething(ctx)
	//  		if err != nil {
	//  			return err
	//  		}
	//  		select {
	//  		case <-ctx.Done():
	//  			return ctx.Err()
	//  		case out <- v:
	//  		}
	//  	}
	//  }
	//
	// See https://blog.golang.org/pipelines for more examples of how to use
	// a Done channel for cancellation.
	Done() <-chan struct{}

Err( )

当Done信道关闭后,Err方法表明Context被撤的原因。

函数签名 Err() error

如果 Done 还没有关闭,Err() 返回nil;如果已经关闭了,Err() 会返回一个非空的error解释为什么关闭。

  • 如果上下文被取消,则返回 Canceled
  • 如果上下文的截止日期已过,则返回 DeadlineExceeded。

Canceled 和 DeadlineExceeded 是两个 error,他们都是由 context.Err() 返回

// Canceled is the error returned by [Context.Err] when the context is canceled.
var Canceled = errors.New("context canceled")

// DeadlineExceeded is the error returned by [Context.Err] when the context's
// deadline passes.
var DeadlineExceeded error = deadlineExceededError{}

func (deadlineExceededError) Error() string   { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool   { return true }
func (deadlineExceededError) Temporary() bool { return true }

当 Err() 返回非空错误之后,连续调用 Err() 会返回相同的错误

	// If Done is not yet closed, Err returns nil.
	// If Done is closed, Err returns a non-nil error explaining why:
	// Canceled if the context was canceled
	// or DeadlineExceeded if the context's deadline passed.
	// After Err returns a non-nil error, successive calls to Err return the same error.
	Err() error

Value( )

Value可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁。

函数签名 Value(key any) any

Value() 返回传入的 key 在这个 context 中关联的 value;如果这个 key 没有关联值则会返回 nil 。连续调用相同的 key 会返回相同的结果。

Values 应该只用作传递跨进程和 API 边界的请求作用域数据,而不是用于传递函数的可选参数。

key 标识 Context 中的特定值。一个函数如果想要在 Context 中存储值,通常会在全局变量中分配一个 key ,然后使用该键作为 context.WithValue 和 Context.Value 的参数。

key 可以是任何支持相等的类型;软件包应将键定义为不导出的类型(变量名小写),以避免碰撞。

	// Value returns the value associated with this context for key, or nil
	// if no value is associated with key. Successive calls to Value with
	// the same key returns the same result.
	//
	// Use context values only for request-scoped data that transits
	// processes and API boundaries, not for passing optional parameters to
	// functions.
	//
	// A key identifies a specific value in a Context. Functions that wish
	// to store values in Context typically allocate a key in a global
	// variable then use that key as the argument to context.WithValue and
	// Context.Value. A key can be any type that supports equality;
	// packages should define keys as an unexported type to avoid
	// collisions.
	//
	// Packages that define a Context key should provide type-safe accessors
	// for the values stored using that key:
	//
	// 	// Package user defines a User type that's stored in Contexts.
	// 	package user
	//
	// 	import "context"
	//
	// 	// User is the type of value stored in the Contexts.
	// 	type User struct {...}
	//
	// 	// key is an unexported type for keys defined in this package.
	// 	// This prevents collisions with keys defined in other packages.
	// 	type key int
	//
	// 	// userKey is the key for user.User values in Contexts. It is
	// 	// unexported; clients use user.NewContext and user.FromContext
	// 	// instead of using this key directly.
	// 	var userKey key
	//
	// 	// NewContext returns a new Context that carries value u.
	// 	func NewContext(ctx context.Context, u *User) context.Context {
	// 		return context.WithValue(ctx, userKey, u)
	// 	}
	//
	// 	// FromContext returns the User value stored in ctx, if any.
	// 	func FromContext(ctx context.Context) (*User, bool) {
	// 		u, ok := ctx.Value(userKey).(*User)
	// 		return u, ok
	// 	}
	Value(key any) any

实现

emptyCtx

emptyCtx 是一个结构体,实现了 Context 接口中的所有方法

emptyCtx 永远不会被取消,没有vlue,没有deadline;他是backgroundCtx 和 todoCtx 的共同基础

// An emptyCtx is never canceled, has no values, and has no deadline.
// It is the common base of backgroundCtx and 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
}

backgroundCtx

结构体中包含了emptyCtx,它的 String 方法返回一个字符串"context.Background"

主要在 context.Background 中被返回

type backgroundCtx struct{ emptyCtx }

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

todoCtx

结构体中包含了emptyCtx,它的 String 方法返回一个字符串"context.TODO"

主要在 context.TODO 中被返回

type todoCtx struct{ emptyCtx }

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

cancelCtx

cancelCtx是一个可以被取消的Context。当它被取消时,它还会取消任何实现了canceler接口的子Context。

  • Context: 嵌入的Context接口,使得cancelCtx可以作为Context使用。
  • mu: 一个互斥锁(sync.Mutex),用于保护以下字段。
  • done: 一个atomic.Value,存储一个chan struct{}类型的通道,这个通道在第一次取消调用时会被关闭。
  • children: 一个映射,存储所有实现了canceler接口的子Context。在第一次取消调用时,这个映射会被设置为nil。
  • err: 一个错误,在第一次取消调用时会被设置为非nil值,表示Context已经被取消。
  • cause: 一个错误,在第一次取消调用时会被设置为非nil值,表示取消的原因。
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
	Context

	mu       sync.Mutex            // protects following fields
	done     atomic.Value          // of 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
	cause    error                 // set to non-nil by the first cancel call
}

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
}

timerCtx

timerCtx 包含一个计时器和截止时间;

timerCtx 嵌入了一个cancelCtx,以实现DoneErr方法。这意味着 timerCtx 可以直接使用 cancelCtxDoneErr方法来处理取消通知和错误状态。

timerCtx实现取消操作时,首先会停止其计时器,然后委托cancelCtx.cancel方法来执行实际的取消操作。这意味着在取消timerCtx时,会先确保计时器不再触发,然后再进行取消操作。

// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.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()
}

valueCtx

valueCtx结构体包含一个键值对,其中keyval字段分别表示键和值

valueCtx实现了Value方法,用于检索与特定键关联的值。对于其他方法的调用,它会委托给嵌入的Context

stringify 函数尝试将任意类型的值转换为字符串,而不使用fmt包。

  • 该函数用于*valueCtx.String()方法中,将键和值转换为字符串表示。
  • 使用类型断言检查值的类型:
    • 如果是实现了stringer接口的类型,调用其String方法。
    • 如果是字符串类型,直接返回该字符串。
    • 如果是nil,返回字符串"<nil>"
    • 其他情况,使用reflectlite.TypeOf(v).String()获取类型名称。

String 方法返回valueCtx的字符串表示。

  • 调用contextName(c.Context)获取父Context的名称。
  • 使用stringify函数将键和值转换为字符串。
  • 拼接这些字符串,形成最终的字符串表示。

Value 方法用于检索与特定键关联的值。

  • 首先检查传入的key是否与valueCtx中的key匹配。
    • 如果匹配,返回对应的值c.val
  • 如果不匹配,调用value(c.Context, key)从父Context中检索值。

value 函数用于在Context链中检索特定键的值。

  • 使用一个无限循环遍历Context链。
  • 使用类型断言检查当前Context的类型:
    • 如果是*valueCtx,检查键是否匹配,如果匹配则返回值,否则继续检查父Context。
    • 如果是*cancelCtx,检查键是否为&cancelCtxKey,如果是则返回Context本身,否则继续检查父Context。
    • 如果是withoutCancelCtx,检查键是否为&cancelCtxKey,如果是则返回nil,否则继续检查父Context。
    • 如果是*timerCtx,检查键是否为&cancelCtxKey,如果是则返回&ctx.cancelCtx,否则继续检查父Context。
    • 如果是backgroundCtxtodoCtx,直接返回nil。
    • 默认情况下,调用当前Context的Value方法继续检索值。
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
	Context
	key, val any
}

// stringify tries a bit to stringify v, without using fmt, since we don't
// want context depending on the unicode tables. This is only used by
// *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 {
				// This implements Cause(ctx) == nil
				// when ctx is created using WithoutCancel.
				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)
		}
	}
}

方法

Background

context.Background 方法返回一个 non-nil 但是 empty 的一个 context。返回的这个context其实就是 emptyCtx ,外加了一个 String 方法

函数调用场景

这个方法主要在 main 函数中使用,作为 Context 树中的最高的节点

// Background returns a non-nil, empty [Context]. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
	return backgroundCtx{}
}

TODO

context.TODO 返回一个 non-nil 但是 empty 的一个context。返回的这个context其实就是 emptyCtx ,外加了一个 String 方法

函数调用场景

  • 不明确使用哪个Context:在某些情况下,开发者可能不清楚应该使用哪个Context。可能因为代码中存在多个Context,而每个Context都有其特定的用途或生命周期,选择错误的Context可能会导致预期之外的行为。
  • Context尚不可用:在代码的早期版本中,当时还没有引入Context作为函数参数。随着代码编写,开发者可能需要将Context参数添加到函数中,以便更好地管理请求的生命周期和取消操作。
// TODO returns a non-nil, empty [Context]. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).
func TODO() Context {
	return todoCtx{}
}

WithCancel

CancelFunc 类型

CancelFunc 用于通知某个操作放弃其工作。调用这个函数意味着操作应该停止执行。

函数本身不会等待操作实际停止。它只是发送一个信号,告诉操作应该停止,但不会阻塞等待操作真正停止。

可以被多个goroutine同时调用。这意味着多个并发执行的goroutine可以独立地决定取消操作

一旦CancelFunc被第一次调用,后续的调用将不会有任何效果。这确保了取消操作只会被执行一次,避免了重复取消的问题。

WithCancel 函数

函数签名 func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel函数返回一个基于父Context的新Context实例,并附带一个新的Done通道。

返回的Context的Done通道会在以下两种情况之一发生时关闭:

  1. 返回的cancel函数被调用。
  2. 父Context的Done通道被关闭。

取消这个Context会释放与之关联的资源,因此代码应该在运行在这个Context中的操作完成后立即调用cancel函数。

// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// A CancelFunc may be called by multiple goroutines simultaneously.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this [Context] complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := withCancel(parent)
	return c, func() { c.cancel(true, Canceled, nil) }
}

WithCancelCause

CancelCauseFunc是一个用于取消操作并设置取消原因的函数类型,WithCancelCause函数用于创建一个可以被取消并记录原因的Context。

CancelCauseFunc 类型

// A CancelCauseFunc behaves like a [CancelFunc] but additionally sets the cancellation cause.
// This cause can be retrieved by calling [Cause] on the canceled Context or on
// any of its derived Contexts.
//
// If the context has already been canceled, CancelCauseFunc does not set the cause.
// For example, if childContext is derived from parentContext:
//   - if parentContext is canceled with cause1 before childContext is canceled with cause2,
//     then Cause(parentContext) == Cause(childContext) == cause1
//   - if childContext is canceled with cause2 before parentContext is canceled with cause1,
//     then Cause(parentContext) == cause1 and Cause(childContext) == cause2
type CancelCauseFunc func(cause error)
  • A CancelCauseFunc behaves like a [CancelFunc] but additionally sets the cancellation cause.

    • CancelCauseFunc是一个函数类型,类似于CancelFunc,但它还额外设置了取消的原因(cause)。
  • This cause can be retrieved by calling [Cause] on the canceled Context or on any of its derived Contexts.

    • 这个取消原因可以通过调用Cause函数在已取消的Context或其派生的任何Context上检索。
  • If the context has already been canceled, CancelCauseFunc does not set the cause.

    • 如果Context已经取消,CancelCauseFunc不会设置原因。
  • For example, if childContext is derived from parentContext:

    • 例如,如果childContext是从parentContext派生的:
      • 如果parentContextchildContext之前被用cause1取消,那么Cause(parentContext) == Cause(childContext) == cause1
      • 如果childContextparentContext之前被用cause2取消,那么Cause(parentContext) == cause1Cause(childContext) == cause2

withCancel 函数

func withCancel(parent Context) *cancelCtx {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	c := &cancelCtx{}
	c.propagateCancel(parent, c)
	return c
}
  • withCancel 函数用于创建一个新的cancelCtx实例,并将其与父Context关联起来。
    • 如果传入的parent为nil,则会引发panic。
    • 创建一个新的cancelCtx实例,并调用propagateCancel方法将新Context与父Context关联起来。

WithCancelCause 函数

// WithCancelCause behaves like [WithCancel] but returns a [CancelCauseFunc] instead of a [CancelFunc].
// Calling cancel with a non-nil error (the "cause") records that error in ctx;
// it can then be retrieved using Cause(ctx).
// Calling cancel with nil sets the cause to Canceled.
//
// Example use:
//
//	ctx, cancel := context.WithCancelCause(parent)
//	cancel(myError)
//	ctx.Err() // returns context.Canceled
//	context.Cause(ctx) // returns myError
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) {
	c := withCancel(parent)
	return c, func(cause error) { c.cancel(true, Canceled, cause) }
}
  • WithCancelCause behaves like [WithCancel] but returns a [CancelCauseFunc] instead of a [CancelFunc].

    • WithCancelCause函数类似于WithCancel,但它返回的是一个CancelCauseFunc而不是CancelFunc
  • Calling cancel with a non-nil error (the “cause”) records that error in ctx; it can then be retrieved using Cause(ctx).

    • 调用cancel函数时传入一个非nil的错误(即“cause”),会将该错误记录在ctx中;可以通过调用Cause(ctx)来检索该错误。
  • Calling cancel with nil sets the cause to Canceled.

    • 如果调用cancel函数时传入nil,则会将原因设置为Canceled
  • Example use:

    • 示例用法:
      ctx, cancel := context.WithCancelCause(parent)
      cancel(myError)
      ctx.Err() // 返回 context.Canceled
      context.Cause(ctx) // 返回 myError
      

Cause

Cause函数用于检索已取消Context的原因。

// Cause returns a non-nil error explaining why c was canceled.
// The first cancellation of c or one of its parents sets the cause.
// If that cancellation happened via a call to CancelCauseFunc(err),
// then [Cause] returns err.
// Otherwise Cause(c) returns the same value as c.Err().
// Cause returns nil if c has not been canceled yet.
func Cause(c Context) error {
	if cc, ok := c.Value(&cancelCtxKey).(*cancelCtx); ok {
		cc.mu.Lock()
		defer cc.mu.Unlock()
		return cc.cause
	}
	// There is no cancelCtxKey value, so we know that c is
	// not a descendant of some Context created by WithCancelCause.
	// Therefore, there is no specific cause to return.
	// If this is not one of the standard Context types,
	// it might still have an error even though it won't have a cause.
	return c.Err()
}
  • Cause 函数返回一个非nil的错误,解释为什么c被取消了。
    • 第一个取消c或其父Context的操作会设置原因。
    • 如果取消是通过调用CancelCauseFunc(err)发生的,那么Cause返回err
    • 否则,Cause(c)返回与c.Err()相同的值。
    • 如果c尚未被取消,则Cause返回nil。

WithDeadline

WithDeadline 返回基于父 Context 的新 Context 实例。这个新 Context 是父 Context 的一个副本,但它具有不同的截止时间。新Context的截止时间被调整为不晚于给定的d时间。d是一个time.Time类型的值,表示截止时间。如果父 context 的 deadline 早于 d ,调用WithDeadline(parent, d)在语义上等同于直接使用父Context。

在以下任一情况下,Context.Done 返回的通道是关闭的:

  • deadline 过期了
  • 返回的 CancelFunc 被调用了
  • 父 context 的 Done channel 被关闭了

取消这个Context会释放与之关联的资源,因此代码应该在运行在这个Context中的操作完成后立即调用cancel函数。WithDeadline函数实际上是调用WithDeadlineCause函数,并将cause参数设置为nil。这意味着在截止时间到达时,不会设置具体的取消原因。

WithDeadlineCause函数类似于WithDeadline,但它还会在截止时间到达时设置返回Context的取消原因。返回的CancelFunc不会设置原因。

// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// [Context.Done] channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this [Context] complete.
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	return WithDeadlineCause(parent, d, nil)
}

// WithDeadlineCause behaves like [WithDeadline] but also sets the cause of the
// returned Context when the deadline is exceeded. The returned [CancelFunc] does
// not set the cause.
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) }
}

WithTimeout

WithTimeout函数实际上是调用WithDeadline函数,并将截止时间设置为当前时间加上超时时间。取消这个Context会释放与之关联的资源,因此代码应该在运行在这个Context中的操作完成后立即调用cancel函数。

WithTimeoutCause函数类似于WithTimeout,但它还会在超时时间到达时设置返回Context的取消原因。返回的CancelFunc不会设置原因。WithTimeoutCause函数实际上是调用WithDeadlineCause函数,并将截止时间设置为当前时间加上超时时间,同时传递一个取消原因。

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

WithValue

WithValue函数返回一个基于父Context的新Context实例,并在该Context中关联keyval

  • 检查父Context是否为nil,如果是则引发panic。
  • 检查键是否为nil,如果是则引发panic。
  • 检查键是否是可比较的(comparable),如果不是则引发panic。
  • 返回一个新的valueCtx实例,该实例包含父Context、键和值。

使用Context的值仅限于请求范围内的数据,这些数据在进程和API之间传递,而不应用于向函数传递可选参数。

// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The provided key must be comparable and should not be of type
// string or any other built-in type to avoid collisions between
// packages using context. Users of WithValue should define their own
// types for keys. To avoid allocating when assigning to an
// interface{}, context keys often have concrete type
// struct{}. Alternatively, exported context key variables' static
// type should be a pointer or interface.
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}
}

关于键的使用建议

提供的键必须是可比较的,并且不应是字符串或其他内置类型,以避免在使用Context的包之间发生冲突。WithValue的用户应该为键定义自己的类型。为了避免在赋值给接口时分配内存,Context的键通常具有具体类型struct{}。或者,导出的Context键变量的静态类型应该是指针或接口。