

新闻资讯
技术学院context.WithCancel 是最直接的取消方式,返回可取消 Context 和 cancel 函数,调用后者立即关闭 ctx.Done();必须配对调用 cancel()(推荐 defer),且需检查 ctx.Err() 区分取消原因。
当需要手动触发取消时,context.WithCancel 是首选。它返回一个可取消的 Context 和一个 cancel 函数,调用后者会立即关闭 ctx.Done() 的 channel。
常见错误是忘记调用 cancel() —— 这会导致 goroutine 泄漏,尤其在循环或高频创建 context 的场景中。
context.WithCancel 都必须配对调用 cancel(),建议用 defer cancel()(除非明确需延迟取消)cancel 传给不可信的第三方函数,它可能被意外调用ctx.Done() 关闭后,再次调用 cancel() 是安全的,但无实际效果ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 确保退出前清理go func() { select { case <-time.After(2 * time.Second): fmt.Println("task done") case <-ctx.Done(): fmt.Println("task canceled:", ctx.Err()) // context.Canceled } }()
time.Sleep(1 * time.Second) cancel() // 主动触发取消
仅监听 ctx.Done() 不足以判断是否真因取消退出——还需检查 ctx.Err(),否则无法区分是超时、取消,还是父 context 被关闭。
典型误用:在 select 中只读 就直接返回,不校验 ctx.Err(),导致日志或错误处理逻辑错判原因。
ctx.Err() 在 ctx.Done() 关闭后才返回非 nil 值;未关闭时为 nil
WithDeadline 到期),子 context 的 Err() 可能是 context.DeadlineExceeded,不是 context.Canceled
ctx.Err() 映射为具体 HTTP 状态码,比如 context.Canc
eled → 499(Client Closed Request)context.WithValue 适合传入请求范围的**不可变元数据**(如 trace ID、user ID),但绝不该用来传递业务参数或可变状态。
容易踩的坑是把 context.Value 当成“全局变量”替代参数传递,结果导致函数签名不透明、单元测试困难、静态分析失效。
string、int、自定义 struct),且 key 类型推荐用未导出的私有类型,避免冲突string 字面量当 key,例如 ctx = context.WithValue(ctx, "user_id", 123) —— 应定义 type ctxKey string; const userIDKey ctxKey = "user_id"
WithValue 有轻微性能开销(需拷贝 map),纯性能敏感路径应避免嵌套多层Go 1.8+ 的 http.Server.Shutdown() 内部使用 context.Context 等待活跃连接关闭,无需手动包装。
常见误区是自己再套一层 WithTimeout 并监听 Done() 来“控制 Shutdown”,反而干扰默认行为,甚至导致连接被强制中断。
srv.Shutdown(ctx) 即可,传入带超时的 context 是合理做法,但别在外部额外 select 监听r.Context().Done()
ctx.Err() == nil,否则无法响应 Shutdown并发任务取消真正难的不是调用 cancel(),而是确保每个分支、每层封装、每次 I/O 都对 ctx.Done() 做响应 —— 尤其是第三方库是否尊重 context,往往得看文档或源码。