直接回答:context 解决跨 API 边界的请求作用域问题:取消信号(调用方不想等了)、超时/截止时间和请求级元数据(trace id 等)沿调用链统一传递。WithCancel/WithTimeout/WithValue 三种派生都返回子 context 和 cancel。公认的纪律:context 作为第一个参数显式传递,不存进结构体;不传递 nil,不确定时用 context.TODO();cancel 必须调用(常用 defer),否则资源泄漏。
展开解析:几个深入要点。取消是协作式的:下游必须主动检查 ctx.Done() 或调用支持 context 的库,否则 cancel 无效果——自己的长循环要周期性 select Done。WithValue 只放请求级只读元数据,别用它传可选参数(类型不安全且隐式依赖),团队里常见的反模式就是把 context 当万能参数袋。派生树是单向的:父取消则子全取消,子取消不影响父。HTTP 框架里 r.Context() 在连接断开时自动取消,数据库驱动、grpc 客户端都吃 context——整条链路打通后超时治理才能生效。静态检查工具(go vet 的 lostcancel、staticcheck 的 SA1012 等)能抓纪律违规,建议接入 CI。
追问方向:context 的取消如何沿树传播?WithDeadline 与 WithTimeout 区别?为什么 http handler 里不应长期使用 r.Context()(如异步任务)?
(约 440 字)