context.Background 和 context.TODO 底层均为 emptyCtx,无行为差异,仅语义不同:Background 表示根上下文,用于 main、顶层 goroutine 或测试;TODO 是开发占位符,须上线前替换,误用将破坏上下文传播与可观测性。

context.Background 和 context.TODO 返回的底层类型完全一样,都是 emptyCtx,不可取消、无截止时间、不携带任何值。它们之间没有运行时行为差异,区别只存在于语义和静态分析层面。
什么时候该用 context.Background
它明确表示「这就是根上下文」,用于程序启动入口或请求生命周期起点:
-
main函数初始化时作为所有子 Context 的父节点 - HTTP 服务中,
http.HandlerFunc接收的req.Context()已有实际含义,但如果你自己新建一个顶层 goroutine(比如后台健康检查),且它不隶属任何请求,则应从context.Background()派生 - 单元测试里需要传入一个合法
Context,又不希望引入取消逻辑干扰断言时,用它最自然
什么时候该用 context.TODO
它是一个带注释性质的占位符,只在开发未完成阶段使用:
- 函数签名刚加了
ctx context.Context参数,但还没想好该传哪个上下文——先填context.TODO(),避免传nil - 团队协作中,你写了一个工具函数,调用方暂时没提供上下文,你不能假设它属于某个请求,就用
TODO标记「此处需后续确认」 - 某些 linter(如
staticcheck)会把未替换的TODO当作警告项,提醒你上线前必须处理
误用的典型后果
两者本身不会导致 panic 或崩溃,但错误选择会影响可维护性和可观测性:
- 在 HTTP handler 内部直接用
context.Background()启动子任务,会导致该任务脱离请求生命周期——请求被 cancel 时它还在跑 - 把
context.TODO()留在生产代码里,等于放弃上下文传播,后续加超时、日志 traceID、权限校验都无从下手 - 用
TODO替代Background在main函数里,会让代码审查者困惑:这是真没想好,还是懒得改?
真正关键的不是选哪个空 Context,而是确保每个 goroutine 都从一个有意义的父 Context 派生——哪怕只是加个 WithValue 塞个 traceID。空 Context 只是起点,不是终点。











