用 go 撸了一个时间轮

最近在网上想找个go的时间轮,用于项目当中。在github上浏览下,没找到特别适合的项目,遂自己撸了一个,顺便学习了。有不足之处,还望大家指教

项目地址:~github.com/welllog/timewheel

简单介绍一下:

先贴一个时间轮数据结构

type TimeWheel struct {
    randomID uint64
    interval time.Duration
    ticker   *time.Ticker

    slotNum    int
    slots      []*list.List
    currentPos int

    onceStart  sync.Once
    addTaskC   chan *task
    stopC      chan struct{}
    taskRecord sync.Map
}

randomID 自增用来作为添加任务的id,而任务id主要用来移除该任务
interval 为时间轮槽的精度,间隔该时间,指向到下一个槽
ticker 为go 的定时器用来驱动时间轮

slotNum 定义时间轮的槽数
slots 时间轮用数组表示,同一槽的任务用链表来存储
currentPos 时间轮当前指向的位置

addTaskC 添加任务将任务发送到该管道中,异步添加,同时防止并发
stopC 时间轮的停止信号通道
taskRecord 用来存储任务,可以通过该map最快o(1)时间复杂度找到任务

type task struct {
   id TaskId
  delay    time.Duration
  circle int
  callback func()
   mut      sync.Mutex
  times int32 //-1:no limit >=1:run times
  async bool
  pool bool
  stop bool
  run bool
}

任务数据结构
移除任务主要将stop标记为true,在轮询到该任务时才会主动删除任务
run 用来表示该任务是否被运行过,在实现timer.Stop方法时很有用

大概用法:

tw := NewTimeWheel(100 * time.Millisecond, 50)
tw.Start()
defer tw.Stop()

tw.AddOnce(time.Second, func() {
 fmt.Println("once")})

 var i int
tw.AddWithTimes(time.Second, 5, func() {
 i++ fmt.Println(i)})

 taskId := tw.AddCron(time.Second, func() {
 i++})

 tw.RemoveAndHasRun(taskId)

 timer := tw.NewTimer(time.Second)
<-timer.C
timer.Reset(500 * time.Millisecond)
timer.Stop()

ticker := tw.NewTicker(500 * time.Millisecond)
var incr int
for {
 <-ticker.C     incr++
 if incr == 20 { ticker.Stop() break }}

 <-tw.After(time.Second)

tw.AfterFunc(500 * time.Millisecond, func() {


tw.Sleep(time.Second)
~by orinfy
讨论数量: 2

感觉没有cron那个灵活,好像只能每一个时间周期运行下,不能每天12点执行下。

3年前 评论

@1112 目前还不打算支持每天定时运行。不过现在的方法嵌套下即可实现

delay := expectTime.Sub(time.Now())
tw.AfterFunc(delay, func(){
    tw. AddCron(24 * time.Hour, func(){
       ToDo()
    })
    ToDo()
})
3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!