context.WithTimeout 不能自动终止 goroutine,需在 goroutine 内显式监听 ctx.Done() 并用 select 配合退出;超时从 WithTimeout 调用起计,非 goroutine 启动时。

用 context.WithTimeout 控制 goroutine 生命周期
Go 没有直接给 goroutine 设超时的语法,必须靠 context 主动协作退出。核心是:启动 goroutine 时传入带超时的 ctx,并在内部定期检查 ctx.Done() 是否关闭。
常见错误是只调用 time.AfterFunc 或 time.Sleep 等待,却不真正中断正在运行的逻辑——goroutine 仍会继续执行,只是没人再等它返回。
-
context.WithTimeout返回的ctx在超时后触发ctx.Done(),但不会自动 kill goroutine - 必须在 goroutine 内部显式监听
<ctx>.Done()</ctx>,并配合select退出关键阻塞点(如 channel receive、HTTP request、数据库查询) - 超时时间从
WithTimeout调用那一刻开始计时,不是从 goroutine 启动开始
goroutine 中正确响应 ctx.Done()
响应超时不是“加个 if 判断”就完事,得嵌入到所有可能阻塞的位置。最典型的是 channel 操作和外部调用。
func doWork(ctx context.Context) {
select {
case <-time.After(5 * time.Second):
fmt.Println("work done")
case <-ctx.Done():
fmt.Println("canceled:", ctx.Err()) // ctx.Err() 是 context.Canceled 或 context.DeadlineExceeded
return
}
}如果函数里有多个步骤,每个可能耗时的操作前都应加 select 判断:
立即学习“go语言免费学习笔记(深入)”;
- HTTP 客户端请求必须用
http.Client{Timeout: ...}或传入带ctx的req.WithContext(ctx) - 数据库查询要使用支持
context的驱动方法,比如db.QueryContext(ctx, ...) - 自定义 channel 接收必须写成
select { case v := ,不能直接 <code>v :=
别用 time.After 替代 context 超时
time.After 看似简单,但它无法被主动取消,会一直占用 goroutine 直到超时触发,大量并发时容易堆积 timer 和 goroutine。
对比下面两种写法:
// ❌ 错误:time.After 不可取消,即使 ctx 已 cancel,timer 仍运行
select {
case <-time.After(10 * time.Second):
// ...
case <-ctx.Done():
// ...
}
<p>// ✅ 正确:用 ctx.Done() 驱动,无额外资源开销
select {
case <-time.After(10 * time.Second): // 仅当确定不需要提前取消时才这么用
case <-ctx.Done():
}-
time.After底层启动一个独立 goroutine,超时后发信号到 channel;而ctx.Done()是内存中状态切换,零开销 - 若 goroutine 已因其他原因提前退出,
time.After的 timer 不会被回收,Go runtime 不会自动 GC 它 - 只有当你明确不需要提前中断(比如纯本地计算且不涉及 I/O),才考虑
time.After
超时后如何清理资源
超时不是终点,而是清理起点。很多 bug 出现在超时后还继续写文件、发网络包、释放未加锁的共享变量。
关键原则:所有持有资源(file handle、net.Conn、sql.Rows、mutex)的 goroutine,必须在 ctx.Done() 分支里做清理。
- 已打开的文件用
defer f.Close()不够,需在select的ctx.Done()分支里显式f.Close() - HTTP server handler 中,
ResponseWriter在ctx.Done()后不能再写 body,否则 panic - 使用
sync.Once或atomic.Bool标记是否已清理,防止重复 close 或 unlock
真正难的不是设超时,而是让所有下游调用链都支持 context 传递,并在每层都做合理响应——漏掉任意一环,超时就形同虚设。


















