用golang实现一个时间轮
时间轮是一种常用的时间管理数据结构,它可以将任务按照时间先后顺序排列,并在预定的时间点执行。在golang中,可以使用如下代码实现一个简单的时间轮:
package main
import (
"fmt"
"time"
)
type Task struct {
id int // 任务id
period time.Duration // 执行周期
next time.Time // 下一次执行时间
}
type TimeWheel struct {
ticks []map[int]*Task // 时间轮集合
tickNum int // 时间轮刻度数
tickDur time.Duration // 刻度周期
curTickIdx int // 当前刻度索引
}
func NewTimeWheel(tickNum int, tickDur time.Duration) *TimeWheel {
tw := &TimeWheel{
ticks: make([]map[int]*Task, tickNum),
tickNum: tickNum,
tickDur: tickDur,
curTickIdx: 0,
}
for i := range tw.ticks {
tw.ticks[i] = make(map[int]*Task)
}
go tw.run()
return tw
}
func (tw *TimeWheel) AddTask(task *Task) {
idx, offset := tw.getTickOffset(task.next)
task.next = task.next.Add(offset)
tw.ticks[idx][task.id] = task
}
func (tw *TimeWheel) RemoveTask(task *Task) {
idx, _ := tw.getTickOffset(task.next)
delete(tw.ticks[idx], task.id)
}
func (tw *TimeWheel) run() {
ticker := time.NewTicker(tw.tickDur)
for {
select {
case <-ticker.C:
tw.curTickIdx = (tw.curTickIdx + 1) % tw.tickNum
tasks := tw.ticks[tw.curTickIdx]
for id, task := range tasks {
delete(tasks, id)
task.next = time.Now().Add(task.period)
idx, offset := tw.getTickOffset(task.next)
task.next = task.next.Add(offset)
tw.ticks[idx][id] = task
go func() {
fmt.Printf("Task %d executed at %v\n", task.id, time.Now())
}()
}
}
}
}
func (tw *TimeWheel) getTickOffset(deadline time.Time) (int, time.Duration) {
diff := deadline.Sub(time.Now())
ticks := int(diff / tw.tickDur)
return (tw.curTickIdx + ticks) % tw.tickNum, diff - time.Duration(ticks)*tw.tickDur
}
func main() {
tw := NewTimeWheel(10, time.Second)
task1 := &Task{
id: 1,
period: 3 * time.Second,
next: time.Now().Add(3 * time.Second),
}
task2 := &Task{
id: 2,
period: 5 * time.Second,
next: time.Now().Add(5 * time.Second),
}
tw.AddTask(task1)
tw.AddTask(task2)
time.Sleep(20 * time.Second)
tw.RemoveTask(task1)
}
在上述代码中,TimeWheel结构体表示时间轮,ticks是时间轮集合,tickNum是刻度数,tickDur是刻度周期。Task结构体表示任务,id是任务id,period是执行周期,next是下一次执行时间。NewTimeWheel函数用于创建时间轮,AddTask函数用于添加任务,RemoveTask函数用于删除任务,run函数用于执行任务。getTickOffset函数用于计算任务所在的刻度和偏移时间。在main函数中,我们创建了两个任务,并添加到时间轮中,等待20秒后,我们删除了第一个任务。在运行程序时,可以看到任务在指定时间点执行的日志信息。
原文地址: https://www.cveoy.top/t/topic/qRv 著作权归作者所有。请勿转载和采集!