不使用库实现Golang定时器
不使用库实现Golang定时器 大家好! 如何在不使用第三方库的情况下,每天凌晨1点执行函数? 类似这样:
func RunAtNight() {
for {
do something.....
time.Sleep(time.Second * 1) //但要在每天凌晨1点执行
}
}
谢谢!
6 回复
太棒了! 谢谢!
如果我们需要能够以某种方式取消预定的运行,上述方法也很有用。
计算下一次执行的时间,然后休眠直到该时刻。
func RunAtNight() {
for {
do something.....
// 明天,与当前相同的时间点
tomorrow := time.Now().AddDate(0, 0, 1)
// 下一次执行时间为明天凌晨1点
next := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 1, 0, 0, 0, time.Local)
// 休眠直到指定时间
time.Sleep(time.Until(next))
}
}
对于这类情况,我发现自己通常只睡眠预期时间的一半。这样我就能理想地修正睡眠期间可能发生的任何延迟,同时输出日志行以便知道计时器仍在等待到适当的时间…所以在上述代码示例中,我会改为这样做:
func RunAtNight() {
for {
do something.....
// Tomorrow, same time as now.
tomorrow := time.Now().AddDate(0, 0, 1)
// Next run is tomorrow, at 1 AM
next := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 1, 0, 0, 0, time.Local)
if tu := time.Until(next); tu <= 60 * time.Second {
// log something here
// Sleep until then.
time.Sleep(tu)
else {
// log something here
// Sleep until half of then.
time.Sleep(tu / 2)
}
}
}
你可以使用标准库的 time 包来实现每天凌晨1点执行函数的需求。关键在于计算当前时间到下一个凌晨1点的时间间隔,然后使用 time.Sleep 或 time.After 等待。以下是两种实现方式:
方法1:使用 time.Sleep 循环执行
package main
import (
"fmt"
"time"
)
func RunAtNight() {
for {
now := time.Now()
next := now.Add(time.Hour * 24)
next = time.Date(next.Year(), next.Month(), next.Day(), 1, 0, 0, 0, next.Location())
duration := next.Sub(now)
fmt.Printf("等待 %v 直到下一次执行\n", duration)
time.Sleep(duration)
// 执行你的任务
fmt.Printf("执行任务,当前时间: %v\n", time.Now())
}
}
func main() {
RunAtNight()
}
方法2:使用 time.After 和 select 语句
package main
import (
"fmt"
"time"
)
func RunAtNight() {
for {
now := time.Now()
next := now.Add(time.Hour * 24)
next = time.Date(next.Year(), next.Month(), next.Day(), 1, 0, 0, 0, next.Location())
duration := next.Sub(now)
fmt.Printf("等待 %v 直到下一次执行\n", duration)
<-time.After(duration)
// 执行你的任务
fmt.Printf("执行任务,当前时间: %v\n", time.Now())
}
}
func main() {
RunAtNight()
}
关键点说明:
-
计算下一次执行时间:
- 获取当前时间
now := time.Now() - 计算明天的日期
next := now.Add(time.Hour * 24) - 设置时间为凌晨1点:
time.Date(next.Year(), next.Month(), next.Day(), 1, 0, 0, 0, next.Location())
- 获取当前时间
-
等待时间计算:
- 使用
next.Sub(now)得到需要等待的时间间隔
- 使用
-
执行循环:
- 每次执行完成后重新计算下一次的等待时间
注意事项:
- 这种方法会在每天凌晨1点准时执行一次
- 如果程序在睡眠期间被终止,下次启动时会重新计算等待时间
- 考虑使用
time.Ticker的替代方案可能更精确,但需要更复杂的时间计算
两种方法都能实现你的需求,选择哪种取决于你的具体偏好。方法1使用 time.Sleep 更直观,方法2使用 time.After 可以更容易地与其他通道操作集成。

