定时器原理

news/2025/1/15 23:27:21/

1. 定时器介绍

程序里的定时器主要实现的功能是在未来的某个时间点执行相应的逻辑。在定时器模型中,一般有如下几个定义。 

interval:间隔时间,即定时器需要在interval时间后执行
StartTimer:添加一个定时器任务
StopTimer:结束一个定时器任务
PerTickBookkeeping: 检查定时器系统中,是否有定时器实例已经到期,相当于定义了最小时间粒度。

常见的实现方法有如下几种:

链表
排序链表
最小堆
时间轮 

接下来我们一起看下这些方法的具体实现原理。

 

2. 定时器实现方法

2.1 链表实现


链表的实现方法比较粗糙。链表用于存储所有的定时器,每个定时器都含有interval 和 elapse 两个时间参数,elapse表示当前被tickTimer了多少次。当elapse 和interval相等时,表示定时器到期。

在此方案中,添加定时器就是在链表的末尾新增一个节点,时间复杂度是 O(1)。

如果想要删除一个定时器的话,我们需要遍历链表找到对应的定时器,时间复杂度是O(n)。

此方案下,每隔elapse时间,系统调用信号进行超时检查,即PerTickBookkeeping。每次PerTickBookkeeping需要对链表所有定时器进行 elapse++,因此可以看出PerTickBookkeeping的时间复杂度是O(N)。

可以看出此方案过于粗暴,所以使用场景极少。

2.2 排序双向链表实现


排序双向链表是在链表实现上的优化。优化思路是降低时间复杂度。

首先,每次PerTickBookkeeping需要自增所有定时器的elapse变量,如果我们将interval变为绝对时间,那么我们只需要比较当前时间和interval时间是否相等,减少了对每个定时器的操作。

如果不需要对每个定时器进行操作,我们将定时器进行排序,那么每次PerTickBookkeeping都只需要判断第一个定时器,时间复杂度为O(1)。

相应的,为了维持链表顺序,每次新增定时器需要进行链表排序时间复杂度为 O(N)。

每次删除定时器时,由于会持有自己节点的引用,所以不需要查找其在链表中所在的位置,所以时间复杂度为O(1),双向链表的好处。

图1 双向链表实现示意图

 

2.3 时间轮实现


相信上一篇文章《构建企业级业务高可用的延时消息中台》我们已经对时间轮有了很深刻的了解。时间轮示意图如下:

图2 时间轮

时间轮的数据结构是数组 + 链表。 

他的时间轮为数组,新增和删除一个任务,时间复杂度都是O(1)。

PerTickBookkeeping每次转动一格,时间复杂度也是O(1)。

2.4 最小堆实现


最小堆是堆的一种, (堆是一种二叉树), 指的是堆中任何一个父节点都小于子节点, 子节点顺序不作要求。

二叉排序树(BST)指的是: 左子树节点小于父节点, 右子树节点大于父节点, 对所有节点适用

 

 

图3 最小堆

树的基本操作是插入节点和删除节点。对最小堆而言,为了将一个元素X插入最小堆,我们可以在树的下一个空闲位置创建一个空穴。如果X可以放在空穴中而不被破坏堆的序,则插入完成。否则就执行上滤操作,即交换空穴和它的父节点上的元素。不断执行上述过程,直到X可以被放入空穴,则插入操作完成。

因此我们可以知道最小堆的插入时间复杂度是O(lgN)。

最小堆的删除和插入逻辑基本类似,如果不做优化,时间复杂度也是O(lgN),但是实际实现方案上,做了延迟删除操作,时间复杂度为O(1)。

延迟删除即设置定时器的执行回调函数为空,每次最小堆超时,将触发pop_heap,pop会重新调整最小堆,最终删除的定时器将调整到堆顶,但是回调函数不处理。

可以看到PerTickBookkeeping只处理堆顶定时器,时间复杂度O(1)。

最小堆可以使用数组来进行表示,数组中,当前下标n的左子节点为2N + 1,当前下标n的右子节点小标为2N + 2。

图4 最小堆的数组表示

 

3. 定时器不同实现对比

3.1 时间复杂度对比


图5 不同实现时间复杂度

从上面的介绍来看,时间轮的时间复杂度最小、性能最好。

3.2 使用场景来看

在任务量小的场景下:最小堆实现,可以根据堆顶设置超时时间,数组存储结构,节省内存消耗,使用最小堆可以得到比较好的效果。而时间轮定时器,由于需要维护一个线程用来拨动指针,且需要开辟一个bucket数组,消耗内存大,使用时间轮会较为浪费资源。

在任务量大的场景下:最小堆的插入复杂度是O(lgN), 相比时间轮O(1) 会造成性能下降。更适合使用时间轮实现。

在业界,服务治理的心跳检测等功能需要维护大量的链接心跳,因此时间轮是首选。


http://www.ppmy.cn/news/820079.html

相关文章

FreeRTOS软件定时器

一、软件定时器简介 1、软件定时器概述 软件定时器允许设置一段时间,当设置的时间到达之后就执行指定的功能函数,被定时器调用的这个功能函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期,简而言之,当定时…

JS定时器

定时器什么是定时器?作用? JS提供了一些原生方法来实现延时去执行某一段代码,下面简单介绍两种计时器。                                                        setTimeOut: setTimeOut(code,millisec,lang)cod…

RT-Thread定时器

目录 定时器管理 定时器超时函数 定时器管理接口 创建定时器 删除定时器 初始化定时器 脱离定时器 启动定时器 停止定时器 控制定时器 定时器执行上下文 定时器管理 定时器,是指从指定的时刻开始,经过一个指定的时间,然后触发一个事…

定时器浅析

定时器浅析 定时器是服务器中一个很重要的部件,比如定时对连接的进行健康检测,在设计协程的sleep API时也需要定时器的介入,因此有效地组织这些定时事件,使之能在预期的时间点被触发且不影响服务器的主要逻辑,对于服务…

通过定时器Timer方式实现时间的精准控制

目录 一、定时器的介绍 1.定时器概念及作用 2.定时器的分类 (1)硬件定时器 (2)软件定时器 (3)系统滴答定时器(SysTick) (4)实时时钟(RTC&am…

MFC定时器的使用

MFC 一.简介 这篇说一下在MFC开发过程当中定时器的使用,我们以在界面上显示系统的时间,让定时器工作,每隔1s时间更新一次 二.使用 1.启动定时器 在我们项目的初始化函数当中,添加初始化代码(OnInitDialog&#x…

apache下载

Apache VS17 binaries and modules download php下载地址 PHP For Windows: Home windows.php.net - /downloads/releases/archives/ 历史版本下载 php下载 https://windows.php.net/downloads/releases/archives/php-5.6.37-Win32-VC11-x64.zip https://www.apachehaus.com/…

BOM——定时器

目录 一.创建定时器 二.停止定时器 三.创建重复调用的定时器 四.案例——发送验证码 一.创建定时器 window.setTimeout(调用函数[,延迟毫秒数])//例子(window可以省略) setTimeout(()> {//执行语句 },2000) setTimeout方法是用来设置定时器的&a…