很长一段时间里,我错误的认识了定时器。无意中,我发现了“时间轮”这个名词,让我对定时器有了新的看法。

我错误的认为,定时器只需要一个 tick 队列,按指定的时间周期遍历队列,检查 tick 倒计时满足触发条件就触发回调。

tick 定义

struct Tick {

int_t n;

func_t func;

};

定时器轮询

void Update()

{

for (auto & tick: _ticks)

{

if (Check(tick))

{

tick.func();

Remove(tick);

}

}

}

实现很简洁,但效率却出奇的慢。

假设有100个tick,依次触发时间是100~10000毫秒,也就是每一个tick的触发间隔为100毫秒。

可以想象,在头100毫秒内,不会有任何tick被触发,但是Update却傻乎乎对100个tick进行Check。

当时间达到100毫秒的时候,只有第一个 tick 达到了触发条件,但是Update依旧会对余下99个进行Check。

时间轮很好的解决了这个问题。

思路是这样的:

轮盘上有若干个插槽

把 tick 放进合适的插槽

每次轮询直接触发插槽里的 tick

实现这个定时器

实现一个 最小刻度为毫秒,最大刻度为天 的定时器(最长延时23点59分59秒999毫秒)

在 3点30分25秒600毫秒 处安插一个 tick

这个实现需要用到4个轮,时(24),分(60),秒(60),毫秒(1000)

总插槽数为 1144 = 24 + 60 + 60 + 1000

首先在 时(3) 安插上这个 tick,

当执行到 时(3) 的时候,删除这个 tick,检查到该 tick 还有 30分25秒600毫秒。

于是在 分(30) 安插上这个 tick,

当执行到 分(30) 的时候,删除这个 tick,检查到该 tick 还有 25秒600毫秒。

于是在 秒(25) 安插上这个 tick,

当执行到 秒(25) 的时候,删除这个tick,检查到该 tick 还有 600毫秒。

于是在 毫秒(600) 安插上这个 tick,

当执行到 毫秒(600) 的时候,删除这个tick,触发这个 tick。

这个 tick 从被安插到被触发,总共只需要 Check(4) 次。

如果采用本文开头的思路,那将会被 Check(天文数字) 次。

lua 实现

sformat = string.format

tinsert = table.insert

tremove = table.remove

tconcat = table.concat

mfloor = math.floor

local utils = require("utils")

local _M = { _slots = nil,

_cycle = nil, }

function _M.Init(self, cycle)

self._slots = {}

utils.tinsert_n(self._slots, {}, 4)

utils.tinsert_n(self._slots[1], {}, 24)

utils.tinsert_n(self._slots[2], {}, 60)

utils.tinsert_n(self._slots[3], {}, 60)

utils.tinsert_n(self._slots[4], {}, 1000)

self._cycle = cycle

end

function _M.Update(self, cycle)

local h1, m1, s1, ms1 = utils.ms2t(self._cycle)

self._cycle = cycle

local h2, m2, s2, ms2 = utils.ms2t(self._cycle)

self:__UpdateT__(24, 1, h1, h2, utils.bind(self.__UpdateH__, self))

self:__UpdateT__(60, 2, m1, m2, utils.bind(self.__UpdateM__, self))

self:__UpdateT__(60, 3, s1, s2, utils.bind(self.__UpdateS__, self))

self:__UpdateT__(1000, 4, ms1, ms2, utils.bind(self.__UpdateMS__, self))

end

function _M.AddTimer(self, delay, func)

self:__Insert__(delay + 1, func)

end

function _M.__Insert__(self, delay, func)

if 0 == delay then

func()

else

local h1, m1, s1, ms1 = utils.ms2t(delay)

local h2, m2, s2, ms2 = utils.ms2t(delay + self._cycle)

local tick = { func = func,

time = { h = h2, m = m2, s = s2, ms = ms2 } }

if h1 ~= 0 then

tinsert(self._slots[1][h2 == 0 and 24 or h2], tick)

elseif m1 ~= 0 then

tinsert(self._slots[2][m2 == 0 and 60 or m2], tick)

elseif s1 ~= 0 then

tinsert(self._slots[3][s2 == 0 and 60 or s2], tick)

elseif ms1 ~= 0 then

tinsert(self._slots[4][ms2 == 0 and 1000 or ms2], tick)

end

end

end

function _M.__UpdateT__(self, cycle, index, first, last, func)

local slots = self._slots[index]

while first ~= last do

first = first + 1

for i = 1, #slots[first] do

func(slots[first][i])

end

slots[first] = {}

first = first % cycle

end

end

function _M.__UpdateH__(self, v)

self:__Insert__(utils.t2ms(0, v.time.m, v.time.s, v.time.ms), v.func)

end

function _M.__UpdateM__(self, v)

self:__Insert__(utils.t2ms(0, 0, v.time.s, v.time.ms), v.func)

end

function _M.__UpdateS__(self, v)

self:__Insert__(utils.t2ms(0, 0, 0, v.time.ms), v.func)

end

function _M.__UpdateMS__(self, v)

self:__Insert__(utils.t2ms(0, 0, 0, 0), v.func)

end

return _M

java时间轮定时器_算法 数据结构——时间轮定时器相关推荐

  1. 时间轮python_算法 数据结构——时间轮定时器

    时间轮定时器 优点:可保证每次执行定时器任务都是O(1)复杂度,在定时器任务密集的情况下,性能优势非常明显. 缺点:内存占用较大,当定时器使用不频繁,处理时间跨度很大的时候,效率低下. C++实现: ...

  2. java线程轮询_基于springboot实现轮询线程自动执行任务

    本文使用: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.使用这种方式可以让你的程序按照某一个频度执行, 但不能在指定 ...

  3. 有向图最长路径算法_算法数据结构 | 三个步骤完成强连通分量分解的Kosaraju算法...

    强连通分量分解的Kosaraju算法 今天是算法数据结构专题的第35篇文章,我们来聊聊图论当中的强连通分量分解的Tarjan算法. Kosaraju算法一看这个名字很奇怪就可以猜到它也是一个根据人名起 ...

  4. tomcat 轮询_用Spring长轮询Tomcat

    tomcat 轮询 就像喜剧演员弗兰基· 豪威尔 ( Frankie Howerd)所说的"噢,小姐小姐" ,但足够多的英国影射和双重诱惑,因为长轮询Tomcat对隔壁的闷气不是某 ...

  5. 一层循环时间复杂度_算法的时间与空间复杂度(一看就懂)

    算法(Algorithm)是指用来操作数据.解决程序问题的一组方法.对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别. 那么我们应该如何去衡量不同 ...

  6. a*算法的时间复杂度_算法的时间和空间复杂度,就是这么简单

    算法(Algorithm) 算法是程序用来操作数据.解决程序问题的一组方法.对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别. 那么我们应该如何去 ...

  7. java当前时间推前三个月_获取当前时间的前三个月 java

    java获取当前路径的几种方法 1.利用System.getProperty()函数获取当前路径:  System.out.println(System.getProperty("user. ...

  8. java.sql.connection 长时间不使用_车子长时间停放不使用,要做到这几点!不然报废是小,安全是大...

    汽车的出现,大大方便了人们的日常出行.加上汽车类型多样.售价多元化,目前大部分家庭都拥有一辆汽车.但有些车主在购车之后,就因为各种各样的原因而"冷落"爱车,动不动就将其放置在一旁不 ...

  9. java 长轮询_基于springboot 长轮询的实现操作

    springboot 长轮询实现 基于 @EnableAsync , @Sync @SpringBootApplication @EnableAsync public class DemoApplic ...

  10. java treeset 红黑树_【数据结构】红黑树与跳表-(SortSet)-(TreeMap)-(TreeSet)

    SortSet 有序的Set,其实在Java中TreeSet是SortSet的唯一实现类,内部通过TreeMap实现的:而TreeMap是通过红黑树实现的:而在Redis中是通过跳表实现的: Skip ...

最新文章

  1. 吴恩达:如何建立一个成功的人工智能创业公司
  2. 移动通信网络频段大全,含5G、4G,各种公开频段汇总
  3. PyTorch模型量化工具学习
  4. java数据库编程(JDBC)
  5. webpack(一) 配置
  6. java如何限制输入值_[限制input输入类型]常用限制input方法
  7. Vue-- $attrs与$listeners的详解
  8. maven package install deploy区别
  9. python dll load failed_python安装MySQLdb的问题 ImportError: DLL load failed
  10. 倾斜摄影和近景摄影技术
  11. Java-web下使用RSA进行加密解密操作
  12. 创 Lockdir 文件夹加密 破解
  13. Apple Pay发展与安全
  14. 自定义快捷键整理 - Windows
  15. Deepin系统安装摄像头驱动
  16. JAVA汽车租赁系统(JAVA毕业设计)
  17. 编程导师Ivor Horton新作《Java 7入门经典》即将出版
  18. AT32F435/437的如何选择选择代码启动地址
  19. vue ui工具来创建vue项目(IDEA)
  20. 前端复习记录(前端基础 JavaScript)一

热门文章

  1. docker镜像制作、数据管理
  2. 解决方案-电子签章:金格科技
  3. java jvm理解_深入理解JVM(一)——基本原理
  4. 开源可视化bi工具有哪些,干货推荐
  5. 计算机中内存、cache和寄存器之间的关系及区别
  6. 数据结构实验一:顺序表的实现及应用
  7. ORACLE rollup函数
  8. ict中的it和ct_ICT.Social – IT专业人员的社交网络
  9. 中国联通:网络重构转型的战略规划及痛点
  10. 决策树和CART决策树