跳到主要内容

Context(上下文)

context.Context 用于在调用树中传播取消信号、截止时间和请求范围的少量元数据。它不是通用配置对象,也不能替代业务参数或错误返回。

取消传播

package main

import (
"context"
"fmt"
"time"
)

func work(ctx context.Context) error {
select {
case <-time.After(100 * time.Millisecond):
fmt.Println("finished")
return nil
case <-ctx.Done():
return ctx.Err()
}
}

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
fmt.Println(work(ctx)) // context deadline exceeded
}

创建 WithCancelWithTimeoutWithDeadline 后必须调用返回的 cancel,即使任务提前成功也要释放关联资源。函数应接收 ctx 作为第一个参数,并在阻塞等待、网络 I/O、数据库调用或循环中定期检查 ctx.Done()

截止时间与失败路径

子 context 的截止时间不能晚于父 context;父 context 取消时,所有子 context 一起取消。收到取消后返回 ctx.Err(),调用方可以用 errors.Is 判断 context.Canceledcontext.DeadlineExceeded

取消是协作式的:它不会强行终止 goroutine。任务必须在每个阻塞点和重试/循环边界响应信号,并关闭自己持有的文件、连接、ticker 或 channel。

WithValue 的边界

WithValue 适合请求 ID、trace ID 等跨层元数据。键应使用私有定义类型,避免包之间碰撞;不要把可选业务参数、配置或大型对象塞进 context。需要业务输入时使用显式函数参数。

常见错误

  • 创建可取消 context 后丢弃 cancel,造成定时器和子节点延迟释放。
  • 只把 context 传入函数,却没有在 I/O、循环或重试中检查取消。
  • 把取消当作强制杀死 goroutine,导致资源泄漏。
  • 使用字符串键或 context 存放任意业务状态。
  • context.Canceled 当作系统故障记录为高优先级错误。

前端的 AbortController 可以帮助理解取消信号,但 Go context 还传播截止时间,并沿调用树统一约束服务端资源生命周期,两者并不等价。

小结

  • context 传播取消、截止时间和少量请求元数据。
  • 每个创建的 cancel 都要调用,每个阻塞任务都要响应取消。
  • context 不替代业务参数、配置或错误返回。