time.Ticker 每到间隔就发信号,不等任务完成;直接 for range ticker.C 调用耗时函数会导致 goroutine 堆积 OOM;应主循环只调度,用带缓冲 channel(如 sem := make(chan struct{}, 5))限流并发数。

time.Ticker 会持续发信号,不等任务执行完就继续触发
这是最常被误读的一点:ticker.C 是一个无缓冲通道,每到间隔时间就往里塞一个 time.Time 值,完全不管上一次启动的 goroutine 是否还在运行。如果你在 for range ticker.C 里直接调用耗时函数,又没做并发控制,就会快速堆积大量 goroutine,最终 OOM。
正确做法是每次收到 tick 后,立即启一个新 goroutine 处理任务,主循环只负责“调度”,不阻塞:
go func() {
for t := range ticker.C {
go func(t time.Time) {
doWork()
}(t)
}
}()
但要注意:这样会无限起 goroutine,没做任何限流或防重入。实际中必须加约束。
用带缓冲的 channel 控制最大并发数
限制同时运行的任务数,最轻量的方式是用带缓冲的 sem channel 当信号量:
-
sem := make(chan struct{}, 5)表示最多 5 个任务并发 - 每次想执行任务前先
select尝试写入sem,成功才起 goroutine - goroutine 结束前必须
归还令牌,否则后续任务永远卡住
关键代码片段:
defer 放在 goroutine 外层,否则归还时机错乱;也不能漏掉 defer,否则 channel 慢慢就满了。
- 定义
done := make(chan struct{}) - 在 goroutine 内用
select监听done和ticker.C - 外部想停任务时,
close(done)或done
典型结构:
doWork()
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











