不一定;仅超时发生时ctx.err()为context.deadlineexceeded,手动cancel或父ctx已取消时均为context.canceled,需同时处理二者。

context.WithTimeout 返回的 ctx 取消后,ctx.Err() 一定是 context.DeadlineExceeded 吗?
不一定。只有超时真正发生时才是;如果手动调用 cancel(),ctx.Err() 是 context.Canceled。很多人误以为 WithTimeout 产生的 ctx 只会返回超时错误,结果在 switch 判断里漏掉 context.Canceled 分支,导致逻辑跳过。
常见错误现象:select 中监听 <code>ctx.Done()
- 必须同时检查
ctx.Err() == context.DeadlineExceeded和ctx.Err() == context.Canceled,不能只判前者 -
context.WithTimeout内部仍依赖用户调用cancel()—— 它只是帮你注册了一个定时器自动调用它而已 - 如果父 ctx 已 cancel,子 ctx 会继承该状态,此时
ctx.Err()也是context.Canceled,和超时无关
用 context.WithCancel 手动控制取消时,忘记调用 cancel() 会导致什么?
goroutine 泄漏。只要 ctx 没被 cancel,所有基于它的 select 或 http.Client 等阻塞操作就可能永远等下去,尤其在 HTTP 请求、channel 接收、数据库查询等场景下非常隐蔽。
使用场景:需要根据业务逻辑(比如收到某个信号、满足某条件)主动终止一组操作时。
立即学习“go语言免费学习笔记(深入)”;
- 务必确保
cancel()在所有路径上都被调用,包括 defer、return 前、error 分支 - 不要把
cancel函数传给不可信的第三方库,除非明确文档说它会调用 - 避免在多个 goroutine 中并发调用同一个
cancel()—— 它是幂等的,但没必要,且容易掩盖流程意图
http.NewRequestWithContext 传入已 cancel 的 ctx,请求真的会立刻中断吗?
不一定立刻。底层 TCP 连接可能还在发包或等 ACK,Go 的 net/http 对 cancel 的响应有延迟,尤其在 TLS 握手、DNS 解析、写请求体阶段。你看到 ctx.Done() 被触发,不代表网络层已经停手。
性能 / 兼容性影响:
- HTTP/1.1 下,cancel 通常会关闭底层连接;HTTP/2 下复用连接,cancel 只标记 stream 失效
- 若服务端已开始处理请求,cancel 不会影响服务端行为,只影响客户端读响应
- 某些旧版 Go(如 Client.CheckRedirect
为什么在函数参数里加 ctx context.Context,却没用 ctx.Done() 或 ctx.Err()?
这是最常见也最危险的“假 Context”用法 —— 参数写了,但没参与控制流。调用方传了带 timeout 的 ctx,你的函数却完全无视,等于把超时能力直接废掉。
关键判断点:
- 只要函数内部有阻塞操作(
time.Sleep、ch 、<code>db.Query、http.Do),就必须监听ctx.Done() - 不要只在函数开头检查
ctx.Err() != nil就 return —— 那只能捕获初始状态,拦不住后续阻塞 - 若调用下游函数(比如另一个带 ctx 的函数),应把当前
ctx透传过去,而不是新建一个context.Background()
复杂点在于:cancel 不是硬中断,它靠协作。每个环节都得主动检查、响应、清理。少一环,整条链就卡住。










