用context控制超时的核心是创建带截止时间的context并传递给协程或HTTP客户端;它提供统一取消信号而非强制终止协程;需用WithTimeout/WithDeadline创建,defer cancel()防泄漏;HTTP请求中通过req.WithContext(ctx)或client.Do生效;协程需在select中监听ctx.Done()协作退出。

用 context 控制超时,核心是创建带截止时间的 context,并在协程或 HTTP 客户端中传递它。Go 的 context 不是为“取消协程”而设计的,而是为“通知下游操作该停止了”提供统一信号机制。
创建带超时的 context
用 context.WithTimeout 或 context.WithDeadline 生成可取消、有时限的 context:
-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)—— 从现在起 5 秒后自动触发 cancel -
defer cancel()必须调用,否则可能泄漏 timer 和 goroutine - 返回的
ctx可安全传给多个函数或 goroutine,它们共用同一个取消信号
在 HTTP 请求中使用超时 context
标准 http.Client 支持传入 context,请求会在 context 超时或取消时立即终止(包括 DNS、连接、TLS、读写全过程):
- 直接用
client.Do(req.WithContext(ctx)),无需额外封装 - 推荐设置
client.Timeout作为兜底,但优先以 context 超时为准(context 更精细、可组合) - 注意:若手动构建
http.Request,务必调用req.WithContext(ctx),原 req 是不可变的
在自定义协程中响应 cancel 信号
协程不能被强制杀死,只能协作式退出。关键是在关键阻塞点检查 ctx.Done():
立即学习“go语言免费学习笔记(深入)”;
- 用
select { case 替代纯time.Sleep - 数据库查询、channel 操作、文件读写等,优先选用支持 context 的版本(如
db.QueryContext、ch := make(chan T, 1); select { case ch ) - 循环中定期检查
if ctx.Err() != nil { return },避免长时间不响应取消
传递 context 时避免常见错误
context 应作为第一个参数显式传入,且不要保存到结构体长期持有(除非明确是 request-scoped 的 handler):
- ❌ 错误:把 context 存进 struct 当字段,后续调用复用旧 context
- ✅ 正确:每个 handler 函数签名以
func(ctx context.Context, ...)开头,由上层注入 - ⚠️ 注意:
context.Background()用于主函数或测试;context.TODO()仅当暂时不确定用哪种 context 时占位
不复杂但容易忽略:超时不是加个 context 就生效,关键在所有参与环节都主动监听和响应 Done 通道。










