GoFrame
GoFrame
的定时方案是采用 优先队列 priority queue 实现的
而 优先队列 priority queue 的实现依赖于 go
原生的 container/heap
GoFrame
内部实现了定时器 Timer
具体路径为 gf/os/gtime/gtimer.go
虽然还有 Cron
的实现 gcron
但也依赖于 gtimer
,所以在此讨论 gtimer
的实现原理
对于 GoFrame
的定时器只用关注几个关键点
Timer.ticks
:初始值为 0, 每个 tick + 1,无限增长// version v2.5.6 if currentTimerTicks = t.ticks.Add(1); currentTimerTicks >= t.queue.NextPriority() { t.proceed(currentTimerTicks) }
Entry.ticks
:表示当前任务的 生命周期,每经过多少个Entry.ticks
执行一次job
Entry.nextTicks
:任务下次执行的时间点,每Entry.ticks
更新一次,也就是job
被执行的时候才会更新// version v2.5.6 entry.nextTicks.Set(currentTimerTicks + entry.ticks)
heap queue
:采用 最小堆(具体见源码)// version v2.5.6 func (h *priorityQueueHeap) Less(i, j int) bool { return h.array[i].priority < h.array[j].priority }
补充的点
GoFrame
内部也是使用了 go
的原生定时器 Timer
func (t *Timer) loop() {
go func() {
var (
....
timerIntervalTicker = time.NewTicker(t.options.Interval)
)
defer timerIntervalTicker.Stop()
for {
...
}
}()
}
robfig/cron
cron
在每个 timer
到时都会 扫描所有的 Entry,将所有到时间的任务全部执行
case now = <-timer.C:
...
for _, e := range c.entries {
...
c.startJob(e.WrappedJob)
...
}
timer
在外部大循环时,总是设置成 第一个任务的超时时间,当没有任务时外部循环会处于长等待
if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
timer = time.NewTimer(100000 * time.Hour)
} else {
timer = time.NewTimer(c.entries[0].Next.Sub(now))
}
其他
持续更新 ...