context.withtimeout会自动触发cancel,无需defer手动调用;手动调用多余且可能panic;仅在非超时需提前终止时调用一次cancel。

context.WithTimeout 会自动触发 cancel 函数,别手动调用
很多初学者在用 context.WithTimeout 时,习惯在 defer 里再显式调用一次返回的 cancel 函数,以为“双重保险”。其实这是错的:WithTimeout 内部已注册定时器,时间一到会自动调用 cancel;手动多调一次不仅多余,还可能引发 panic(如果上下文已被取消)。
正确做法是:只在需要提前终止(比如中间某步出错)时才调用 cancel,且确保只调用一次。常见场景是 HTTP handler 中启动 goroutine 后,主流程提前返回,需主动取消子任务。
- 超时由系统自动触发,无需 defer
cancel - 手动
cancel仅用于非超时路径的提前退出 - 多个 goroutine 共享同一
ctx时,一次cancel即可通知全部
HTTP client 要传入带 timeout 的 context,不是设置 client.Timeout
Go 的 http.Client 自身有 Timeout 字段,但它只控制整个请求生命周期(从 Dial 到读完 body),无法覆盖 DNS 解析、TLS 握手等前置阶段,也不能和外部取消信号联动。而 context.Context 是唯一能统一控制所有环节的方式。
必须把 context 传给 client.Do(req.WithContext(ctx)),否则 ctx 对请求完全无效。注意:不能只改 req.Context() 就完事,要确保最终调用的是 Do 方法接收的那个 context。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
client.Timeout和context是两套机制,优先用 context - 错误写法:
req = req.WithContext(ctx)但调用client.Do(req)—— 这样 context 不生效 - 正确写法:
client.Do(req.WithContext(ctx))或先req = req.WithContext(ctx)再client.Do(req) - DNS 超时、连接池阻塞等场景,只有 context 能覆盖
select + ctx.Done() 是监听取消的唯一可靠方式
在自定义 long-running 操作(如轮询、流式处理)中,不能靠“检查 ctx.Err() != nil”来判断是否该退出——这只能捕获已发生的取消,无法及时响应。必须用 select 配合 ctx.Done() 通道,在 goroutine 阻塞或等待时也能被唤醒。
例如循环读取 channel 时,若只在每次循环开头检查 ctx.Err(),那当前读操作仍会卡住;而用 select 把 ctx.Done() 和数据 channel 并列监听,就能真正实现“随时可中断”。
-
ctx.Done()是一个只读 channel,关闭后会立即可读 - 永远用
select等待它,而不是轮询ctx.Err() - 如果操作本身支持 context(如
database/sql.Rows.Next()),优先用其带 context 的变体 - 自己封装阻塞操作时,记得在关键等待点插入
select分支
context.WithCancel 和 WithTimeout 的 parent 必须可取消
如果父 context 是 context.Background() 或 context.TODO(),那基于它创建的 WithCancel 或 WithTimeout 依然可以正常工作。但一旦父 context 已被取消(比如上游服务提前关掉),子 context 会立即失效——这时你设的 30 秒 timeout 实际上只剩 0 秒。
真实服务链路中,HTTP handler 的 ctx 往往来自 http.Request.Context(),它本身可能已被服务器中断(如客户端断开连接)。所以不要假设你的 timeout 总是“从零开始计时”,得接受它可能被上级压缩甚至归零。
- 用
ctx.Deadline()查看实际剩余时间,比硬编码更稳妥 - 日志中打印
ctx.Err()时,留意是否为context.Canceled(上级取消)而非context.DeadlineExceeded(自身超时) - 测试时用
context.WithCancel(context.Background())手动模拟上级取消,比只测 timeout 更贴近生产环境










