Go ticker 不 Stop 会造成什么资源问题
如果你在 Go 1.23 或更高版本里忘了调用 Ticker.Stop,先别急着把它判定为“ticker 内存泄漏”。官方 time 文档已经说明:当 ticker 不再被引用时,垃圾回收器可以回收它,即使没有调用 Stop。真正容易留下资源问题的情况,是后台 goroutine 还握着 ticker.C,或者 ticker 被放在长期存活的结构体、全局变量里,导致定时工作和业务回调仍然继续。
所以排查要分两条线:Stop 负责关闭定时器的发送,done 或上下文负责让消费 goroutine 返回。只做其中一件,生命周期都可能没有闭合。
- Go 1.23 之后,未被引用且未 Stop 的 ticker 可以被 GC 回收;不能据此推断所有场景都安全。
- 长期持有 ticker 或继续运行的消费 goroutine,才是后台任务重复执行、goroutine 数量上涨的常见来源。
- 可停止的轮询通常同时需要
defer ticker.Stop()和一个明确的退出分支。
不 Stop 先看哪种资源还被持有
把问题拆成三个对象会更容易判断:ticker 本身、它暴露的 C 通道,以及读取通道的 goroutine。它们不是同一个生命周期。
| 观察对象 | 没 Stop 时要问什么 | 常见后果 |
|---|---|---|
| ticker 对象 | Go 1.23 之后是否还有引用? | 无引用时可由 GC 回收;仍被结构体或闭包持有时不会自然消失 |
ticker.C | 是否仍被 select、range 或闭包读取? | 消费方继续等待 tick,资源边界被长期持有 |
| 消费 goroutine | 是否有 done、context 或返回条件? | goroutine 不退出,回调可能继续执行或重复执行 |
Stop 之后不会关闭 C 通道。这不是遗漏,而是标准库刻意保留的语义:关闭通道可能让并发读取方把“没有新 tick”误判成零值事件,也会影响后续 Reset。因此不要用“等通道关闭”作为 ticker 的退出协议。

循环里真正危险的是没有退出入口
下面这种写法的问题不只是漏写 Stop:
func startPolling() {
ticker := time.NewTicker(time.Minute)
go func() {
for range ticker.C {
refreshCache()
}
}()
}
调用 startPolling 后,创建者没有保存停止函数,goroutine 也没有等待上下文。只要它还在运行,ticker.C 就有消费者;即使某个版本的 GC 能回收没有引用的 ticker,也不能替你结束这个 goroutine,更不能取消 refreshCache 已经启动的业务动作。
这类代码常见于 HTTP 服务启动钩子、重载配置和测试用例。每调用一次就再创建一条长期循环,表面上像“偶尔多执行一次”,实际是轮询任务的所有权没有落到可管理的对象上。
用 done 和 Stop 把轮询循环关完整
一个小而完整的写法,是让创建者同时拥有关闭信号和 ticker 的释放责任:
func runPolling(done
这里有两个互补动作:收到 done 后,return 让 goroutine 结束;函数离开时,defer 执行 Stop。如果 processBatch 本身会阻塞,仍需把 done 或 context.Context 继续传进去,否则外层循环能退出,业务调用却可能卡住。
如果你只想获得一个永不主动停止的 channel,time.Tick 是便利封装;但需要控制生命周期的后台任务,更适合保留 *time.Ticker,因为它能表达“谁负责停止”。

修复后用三项检查确认没有假恢复
不要只看代码里出现了 defer ticker.Stop()。修复后可以按下面的清单复查:
- 重复启动入口:确认服务重载、测试 setup 或请求处理器不会反复创建同一类轮询 goroutine。
- 退出信号:触发关闭时,
done或 context 确实能到达select,而不是只设置了一个没人读取的标志位。 - 业务回调:停止后不应再看到新的轮询回调;如果回调可能并发执行,要把“循环退出”和“当前回调完成”分开记录。
排查时可用 goroutine profile 或测试里的计数器观察趋势,但不要把 goroutine 数量一次升高就直接归因给 ticker。先确认是否有重复启动,再确认旧 goroutine 是否收到退出信号,最后才看 ticker 是否仍被长生命周期对象引用。
常见问题
Go 1.23 以后还要不要写 ticker.Stop?
要。它不再是“为了让 GC 回收 ticker”这一项硬性要求,但仍然是表达业务停止、减少无意义 tick 和收束资源所有权的清晰写法。
Ticker.Stop 会关闭 ticker.C 吗?
不会。停止后不再发送 tick,消费 goroutine 仍需要通过 done、context 或其他协议返回。
time.Tick 和 time.NewTicker 怎么选?
固定运行且不需要主动停止时可以用 time.Tick;需要取消、重置或由结构体管理生命周期时使用 time.NewTicker 并保存指针。
只补 Stop 就能解决 goroutine 泄漏吗?
不能。必须同时找到 goroutine 的退出入口;如果它在等待 ticker.C,停止 ticker 也不会自动关闭通道。
MySQL JSON_VALUE 怎么把 JSON 字段直接建表达式索引
- 上一篇
- MySQL JSON_VALUE 怎么把 JSON 字段直接建表达式索引
- 下一篇
- 暂无
-
- Golang · Go问答 | 1小时前 | select · go · nil channel · channel阻塞 ·
- Go 从 nil channel 接收为什么程序像卡死一样
- 291浏览 收藏
-
- Golang · Go问答 | 2小时前 |
- Go 取 range 变量地址为什么得到重复指针
- 501浏览 收藏
-
- Golang · Go问答 | 3小时前 | 并发 · map · go · race detector ·
- Go map 只读并发为什么也可能触发数据竞态
- 207浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 167次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 93次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 16次使用
-
- LangGPT
- LangGPT是一种受编程语言启发的结构化提示词设计工具,提供双层框架、模块化模板及变量功能,帮助用户高效编写高质量Prompt。该项目已在GitHub免费开源,适用于内容创作、编程辅助等多场景。
- 29次使用
-
- ClickPrompt
- ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
- 61次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go 取 range 变量地址为什么得到重复指针
- 2026-09-07 501浏览
-
- Go net.Conn 写入超时为何仍会卡住:SetWriteDeadline、部分写入与连接复用检查
- 2026-08-30 501浏览
-
- Go 问答:httptrace.ClientTrace GotConnInfo 怎么判断连接是否复用:连接池与请求时序边界
- 2026-08-28 501浏览

