用时间轮来实现定时器时,需要定义每一个格子的“刻度”,可以将时间想象成一个时钟,中心有秒针顺时针转动,每次转动到一个刻度时,需要去查看该刻度挂载的任务列表是否已经有到期的任务。

从结构上来讲,时间轮和哈希表很相似,如果把哈希算法定义为:触发时间 % 时间轮元素大小,那么这就是一个简单的哈希表。在哈希冲突时,采用链表挂载哈希冲突的定时器。

除了这种单层时间轮,还有一些时间轮采用多层实现。

用 Go 实现的时间轮项目不多,下面这个是其中性能较好的时间轮的延时任务示例:

package main

import (
    "github.com/antlabs/timer"
)

// 一次性定时器
func after(tm timer.Timer) {
    var wg sync.WaitGroup
    wg.Add(2)
    defer wg.Wait()

    go func() {
        defer wg.Done()
        tm.AfterFunc(1*time.Second, func() {
            log.Printf("after 1 second\n")
        })
    }()

    go func() {
        defer wg.Done()
        tm.AfterFunc(10*time.Second, func() {
            log.Printf("after 10 seconds\n")
        })
    }()

    go func() {
        defer wg.Done()
        tm.AfterFunc(30*time.Second, func() {
            log.Printf("after 30 seconds\n")
        })
    }()

    go func() {
        defer wg.Done()
        tm.AfterFunc(time.Minute, func() {
            log.Printf("after 1 minute\n")
        })
    }()

    go func() {
        defer wg.Done()
        tm.AfterFunc(time.Minute+30*time.Second, func() {
            log.Printf("after 1 minute and 30 seconds\n")
        })
    }()

    go func() {
        defer wg.Done()
        tm.AfterFunc(2*time.Minute+45*time.Second, func() {
            log.Printf("after 2 minutes and 45 seconds\n")
        })
    }()
}

// 周期性定时器
func schedule(tm timer.Timer) {
    tm.ScheduleFunc(500*time.Millisecond, func() {
        log.Printf("schedule 500 milliseconds\n")
    })

    tm.ScheduleFunc(time.Second, func() {
        log.Printf("schedule 1 second\n")
    })

    tm.ScheduleFunc(20*time.Second, func() {
        log.Printf("schedule 20 seconds\n")
    })

    tm.ScheduleFunc(1*time.Minute, func() {
        log.Printf("schedule 1 minute\n")
    })
}

func main() {
    tm := timer.NewTimer()
    defer tm.Stop()

    // go after(tm)
    go schedule(tm)

    go func() {
        time.Sleep(2*time.Minute + 50*time.Second)
        tm.Stop()
    }()

    tm.Run()
}
最后编辑: kuteng  文档更新时间: 2022-03-22 19:29   作者:kuteng