贝利信息

Go错误处理如何结合context使用_Go超时与取消处理

日期:2026-01-14 00:00 / 作者:P粉602998670
context.WithTimeout返回ctx和cancel函数,必须显式调用cancel以释放资源;HTTP请求需传入ctx并用NewRequestWithContext封装;阻塞操作应配合select+ctx.Done()监听取消信号。

context.WithTimeout 会自动触发 cancel 函数

调用 context.WithTimeout 不仅返回带超时的 ctx,还会返回一个 cancel 函数。这个 cancel 必须被显

式调用(或让 defer 触发),否则底层定时器不会释放,可能造成 goroutine 泄漏。

HTTP client 请求必须传入 context.Context

Go 标准库的 http.Client 支持通过 DoGet 等方法接收带取消信号的 ctx。不传 ctx 就无法响应上游取消或超时,请求会卡死直到 TCP 层超时(通常数分钟)。

req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com/data", nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
        // 处理超时或取消
    }
    return err
}

select + ctx.Done() 是阻塞操作的标准退出模式

任何可能长期阻塞的操作(如 channel receive、time.Sleep、数据库查询)都应配合 ctx.Done() 使用 select,避免 goroutine 卡住无法响应取消。

select {
case <-time.After(5 * time.Second):
    return "done"
case <-ctx.Done():
    return ctx.Err().Error() // 可能是 Canceled 或 DeadlineExceeded
}

自定义操作中传递并检查 ctx.Err() 是关键习惯

你写的函数若涉及 I/O、重试、循环等待等,必须在每轮迭代开头检查 ctx.Err() != nil,否则即使父级已取消,子逻辑仍会继续执行。

实际项目中最容易漏掉的是:在 defer 中调用 cancel() 后,又在函数中间因错误提前 return,导致后续逻辑没机会执行 defer —— 正确做法是把 cancel 放在最外层 defer,或用命名返回值+defer 统一处理。