Context超时检查必须监听ctx.Done()通道,不可依赖ctx.Err()提前判断;标准库操作需传入带超时的context并检查errors.Is(err, context.DeadlineExceeded);自定义循环中应节制轮询Done()并最终确认Err()。

Context超时检查的核心是监听 Done() 通道
Go 中的 context.Context 不提供“主动查询是否已超时”的方法,必须通过监听 ctx.Done() 通道来感知状态变化。调用 ctx.Err() 只能在 Done() 关闭后获取具体原因(比如 context.DeadlineExceeded),不能提前预判。
常见错误是写成:if ctx.Err() != nil { ... } —— 这不可靠,因为 Err() 在超时刚发生但 goroutine 尚未调度到该判断时仍可能返回 nil,造成竞态漏判。
正确做法始终是:用 select 等待 ctx.Done(),再调用 ctx.Err() 获取确切错误类型。
如何在阻塞操作中安全响应超时(如 HTTP 请求、数据库查询)
多数 Go 标准库和主流 SDK(如 net/http、database/sql、redis.Client)都接受 context.Context 参数,并在上下文取消或超时时自动中断操作。关键在于传入的是带超时的 context,且调用方不忽略返回错误。
- 用
context.WithTimeout(ctx, time.Second*5)或context.WithDeadline创建子 context - 把该 context 传给支持 context 的函数(如
http.Client.Do(req.WithContext(ctx))) - 检查返回 error 是否为
context.DeadlineExceeded或context.Canceled,而不是只看err != nil
示例:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Println("请求超时")
}
return err
}
ctx.Err() 返回值的含义与典型判断方式
ctx.Err() 只有在 ctx.Done() 已关闭后才有意义;在此之前它始终返回 nil。因此不能脱离 select 或已知完成状态单独使用。
典型判断逻辑应为:
-
errors.Is(err, context.DeadlineExceeded):明确由超时触发(推荐,兼容性好) -
errors.Is(err, context.Canceled):由手动cancel()触发 - 避免直接比较
err == context.DeadlineExceeded,因底层可能包装错误 - 不要用
err.Error()字符串匹配,脆弱且不跨版本
自定义长时间操作中嵌入超时检查(非标准库调用)
当你写一个可能耗时的循环或计算逻辑,且无法直接传 context 给底层函数时,需手动轮询 ctx.Done()。
常见陷阱:
- 轮询太频繁(如每次迭代都
select)影响性能 → 改为每 N 次迭代或每固定时间间隔检查一次 - 忘记在循环外最后检查一次
ctx.Err()→ 循环结束后仍要确认是否被取消 - 误用
time.Sleep阻塞而忽略 context → 应改用time.AfterFunc或在select中同时等待 sleep 和ctx.Done()
示例(带节流的轮询):
select
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











