理解Context
这篇文章介绍的很清楚:深入理解Go Context
这个比较详细,但是层次不好:理解GO CONTEXT机制
关于context的使用场景
context的主要使用场景在于:一个任务在处理的过程中可能会启动很多个协程来进行处理。在这个过程中,如果上游的任务想要取消,下游的任务也应当一起取消。context的任务就来了。
内容介绍
context包的内容可以概括为:1个接口,4个实现,6个方法
接口
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
,以实现Done
和Err
方法。这意味着 timerCtx
可以直接使用 cancelCtx
的Done
和Err
方法来处理取消通知和错误状态。
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
结构体包含一个键值对,其中key
和val
字段分别表示键和值
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。 - 如果是
backgroundCtx
或todoCtx
,直接返回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通道会在以下两种情况之一发生时关闭:
- 返回的
cancel
函数被调用。 - 父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
不会设置原因。
- 如果Context已经取消,
For example, if childContext is derived from parentContext:
- 例如,如果
childContext
是从parentContext
派生的:- 如果
parentContext
在childContext
之前被用cause1
取消,那么Cause(parentContext) == Cause(childContext) == cause1
。 - 如果
childContext
在parentContext
之前被用cause2
取消,那么Cause(parentContext) == cause1
且Cause(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中关联key
和val
。
- 检查父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键变量的静态类型应该是指针或接口。