Go: 标准库 context

2022-03-262026-07-101 引用

传播链的生命周期,跨边界的上下文

context 包定义了一个上下文Context类型:这个类型在跨越 API 边界和进程之间,携带了超时,取消信号,以及其他请求域内的值;

本质:一条调用链/传播链上各个节点共享一套:是否超时、是否取消、值的机制。

Context 接口

go
type Context interface {
	// 返回截止时间,ok 为 false 表示没有设置 deadline
	// ! 未来什么时候结束,还未结束
	Deadline() (deadline time.Time, ok bool)
 
	// 返回一个 channel,当 context 被取消或超时时关闭;永远不会取消的 context 返回 nil
	// ! 已经结束
	Done() <-chan struct{}
	// Done 未关闭,返回 nil;已关闭返回原因(Canceled 或 DeadlineExceeded)
	Err() error
 
	// 返回 key 关联的值,不存在返回 nil
	Value(key any) any
}

context 是接受信号的一方进行协同操作。

派生树模型

派生树模型,在父ctx上派生出子ctx

当一个 Context 被取消时,所有从它派生的子 Context 也会被取消

根 Context

  • context.Background() — 通常用于 main、init 或顶层请求入口
  • context.TODO() — 不确定用哪个 Context 时的占位符

派生函数

函数 作用
WithCancel(parent) 返回可手动取消的子 context 和 cancel 函数
WithDeadline(parent, d) 到达时间点 d 自动取消
WithTimeout(parent, t) 经过时长 t 自动取消(等价于 WithDeadline(parent, time.Now().Add(t))
WithValue(parent, key, val) 附带一个 key-value 对,用于请求域数据的传递

WithoutCancel(parent) 返回一个不会随父 context 取消而取消的派生 context AfterFunc(ctx, f) ctx 取消后,在新 goroutine 中执行 f(Go 1.21)

Cause 的 API: context 被取消时,通过Cause获取具体原因

函数 作用
WithCancelCause(parent) 在普通取消基础上额外记录一个自定义 error 原因(Go 1.20+)
WithDeadlineCause(parent, d, cause) 在 deadline 取消基础上额外记录一个自定义 error 原因
WithTimeoutCause(parent, t, cause) 在 timeout 取消基础上额外记录一个自定义 error 原因
Cause(ctx) 读取 context 被取消的具体原因;若未显式指定 cause,则返回值通常与 ctx.Err() 一致

错误值

调用 Err() 返回

  • context.Canceled — 手动调用 cancel 函数时
  • context.DeadlineExceeded — deadline/timeout 到期时

取消的原因

谁先 cancel,谁定义原因cause

NOTE: 为什么 WithTimeoutCause/WithDeadlineCause 不返回 CancelCauseFunc:因为手动 cancel → 原因变成 context.Canceled

使用规则

  1. 不要把 Context 存在 struct 里显式作为函数第一个参数传递,通常命名 ctx
  2. 不要传 nil Context,不确定时用 context.TODO()
  3. Value 只用于请求域数据(如 trace ID、认证信息),不要用来传可选参数
  4. Context 是并发安全的,多个 goroutine 可同时使用
  5. cancel 函数调用后要尽快执行(通常 defer cancel()),避免资源泄漏

Go 中 context.Value 应该放什么

如果你的代码没有这个值就无法运行,那这个值就不应该放进 context。 context 是附带信息的通道,不是参数传递的替代品

  1. https://go.dev/blog/context

评论