Go 的 context 是用于解决生产中 goroutine 泄漏、超时失控、取消不一致等问题的控制机制,而非并发入门工具;它通过外部干预管理已有 goroutine 生命周期, misuse(如传参、滥用 WithValue)比不用更危险;Done() 必须配合 select 使用,不可直接读取。

Go 的 context 不是用来“学习并发”的入门工具,而是解决真实服务中 goroutine 泄漏、超时失控、取消不一致等生产问题的控制机制。 它不是并发模型本身,而是对已有 goroutine 生命周期的外部干预手段。用错地方(比如传参、存结构体、滥用 WithValue)比不用更危险。
context.Done() 必须配合 select 使用,不能直接读取
很多人写 if 或直接 <code>ctx.Done() 取值,这会立即阻塞或 panic。Done 返回的是一个只读通道,必须用 select 非阻塞监听。
- 正确姿势:始终在循环或等待逻辑中用
select响应ctx.Done(),例如网络读、数据库查询、定时轮询 - 错误现象:
fatal error: all goroutines are asleep - deadlock,因为单独读未关闭的 channel 会永久阻塞 - 性能影响:
select是 Go 运行时优化过的非抢占式调度点,不会引入额外锁或系统调用
WithTimeout 和 WithDeadline 的时间参数必须是相对/绝对时间,不能传 0 或负数
context.WithTimeout(ctx, 0) 不等于“不超时”,它会立刻触发取消;context.WithDeadline(ctx, time.Now().Add(-1*time.Second)) 同样立即失效。这两个函数内部会校验时间有效性,非法值导致上下文一创建就关闭 Done 通道。
- 常见误用:从配置读取超时秒数后未做校验,如配置为
"0"或"-1",结果所有请求瞬间失败 - 安全做法:用
time.ParseDuration解析字符串,并加if d 默认兜底 - 兼容性注意:Go 1.20+ 对负超时的 Err 返回更明确(
context.DeadlineExceeded),但行为不变——仍是立即取消
WithValue 只能传元数据,不能传业务参数或结构体指针
context.WithValue 的键类型必须可比较(string、int、自定义空 struct),且值应轻量、只读。传 *sql.DB、http.Client 或含 mutex 的 struct,会导致竞态或内存泄漏。
立即学习“go语言免费学习笔记(深入)”;
- 典型场景:仅用于透传
request_id、user_id、trace_id等字符串标识 - 容易踩的坑:把
func类型或闭包塞进 Value,跨 goroutine 调用时捕获了意外变量,引发隐蔽 bug - 替代方案:业务参数请走函数显式参数;资源对象请通过依赖注入(如 struct 字段)传递,而非 context
真正难的不是写对那几行 ctx, cancel := context.WithTimeout(...),而是判断该在哪一层派生 context、何时调用 cancel()、以及为什么某个子 goroutine 没响应取消信号——这些都藏在调用链的深度和资源持有关系里。


















