不会主动阻塞,但

time.After 会阻塞 goroutine 吗?
不会主动阻塞,但 time.After 返回的是一个 ,如果你用 在主线程或 goroutine 中直接接收,就会**同步阻塞直到超时或被中断**——这是最常被误解的一点。
典型错误写法:
time.After(5 * time.Second)
-
time.After底层调用time.NewTimer,每次调用都新建一个 timer,不复用 - 频繁创建 + 不触发 = 大量 goroutine 挂在 runtime timer heap 上,GC 清不掉
- 真实压测中,每秒调用 1000 次
time.After,几分钟后runtime.timer数量飙升至数万
替代方案:用 time.NewTimer + Stop 避免泄漏
需要手动管理 timer 生命周期时,优先选 time.NewTimer,它允许你在不触发时显式 Stop(),释放底层资源。
正确写法示例:
t := time.NewTimer(5 * time.Second)
defer t.Stop() // 即使超时了也要 defer,防止 panic: stop on stopped timer
<p>select {
case </p>
-
t.Stop()返回bool:true 表示 timer 未触发且已停,false 表示已触发或已停(安全,可多次调用) - 必须在
select后、或任意分支退出前调用t.Stop(),否则泄漏 - 如果
done先到达,t.C就永远不会被读取,此时t.Stop()是唯一释放途径
高频场景下该用 time.AfterFunc 还是 timer?
time.AfterFunc 适合「只执行一次回调」的轻量超时通知,不涉及通道通信;但它**无法取消已注册但未执行的函数**——一旦注册,timer 就不可撤回。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
对比选择建议:
- 只需“到点干件事”,且不关心是否能取消 → 用
time.AfterFunc(d, f) - 需要和其它 channel 一起 select、可能提前退出、或需复用/重置 → 用
time.NewTimer或time.NewTicker - 想彻底避免手写
Stop逻辑?考虑封装一个带 cancel 的 helper:
context.WithTimeout
- 设置
http.Client.Timeout(整个请求生命周期) - 或传入带超时的
context.Context到Do:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() resp, err := http.DefaultClient.Do(req.WithContext(ctx))
注意:context.WithTimeout 底层也是基于 time.Timer,但它由 context 包统一管理生命周期,无需手动 Stop。
真正容易被忽略的是:超时后,底层 TCP 连接可能还在发包或等 ACK,context 只负责通知上层取消,不强制 kill socket —— 如果你看到超时后仍有网络延迟抖动,那是正常现象,不是 bug。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










