0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一文读懂定时器实现技术

jf_78858299 来源:架构之美 作者: 孙玄 架构之美 2023-04-21 14:36 次阅读

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 时间轮实现

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

他的时间轮为数组,新增和删除一个任务,时间复杂度都是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) 会造成性能下降。更适合使用时间轮实现。

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 定时器
    +关注

    关注

    23

    文章

    3229

    浏览量

    114288
  • 程序
    +关注

    关注

    116

    文章

    3753

    浏览量

    80722
收藏 人收藏

    评论

    相关推荐

    555定时器

    555定时器555定时器555定时器555定时器555定时器555定时器555
    发表于 11-10 17:25 52次下载

    基于STM32定时器实现毫秒延时函数

    STM32定时器包含基本定时器、通用定时器和高级定时器,其中TIM6和TIM7是STM32当中的基本定时器,作为初学者,先从最基本的学起最容
    发表于 10-12 15:54 2.5w次阅读
    基于STM32<b class='flag-5'>定时器</b><b class='flag-5'>实现</b>毫秒延时函数

    定时器原理以及定时器实现的方式

    定时器原理定时器实现的方式有以下几种: 基于排序链表方式: 通过排序链表来保存定时器,由于链表是排序好的,所以获取最小(最早到期)的
    的头像 发表于 08-14 11:15 6687次阅读

    STM32基于cubeMX实现定时器点灯

    概述STM32的常见的定时器资源: 系统嘀嗒定时器SysTick、看门狗定时器WatchDog、实时时钟RTC、基本定时器、通用定时器、高级
    发表于 11-23 18:21 19次下载
    STM32基于cubeMX<b class='flag-5'>实现</b><b class='flag-5'>定时器</b>点灯

    STM32定时器-基本定时器

    ,分为基本定时器,通用定时器和高级定时器。基本定时器 TIM6 和 TIM7 是个 16 位的只能向上计数的
    发表于 11-23 18:21 31次下载
    STM32<b class='flag-5'>定时器</b>-基本<b class='flag-5'>定时器</b>

    STM32——高级定时器、通用定时器、基本定时器的区别

    STM32——高级定时器、通用定时器、基本定时器的区别
    发表于 11-26 15:21 110次下载
    STM32——高级<b class='flag-5'>定时器</b>、通用<b class='flag-5'>定时器</b>、基本<b class='flag-5'>定时器</b>的区别

    SysTick 定时器

    11.1关于 SysTick 定时器SysTick定时器(又名系统滴答定时器)是存在于Cortex-M3的定时器,只要是ARM Cote
    发表于 12-05 14:51 9次下载
    SysTick <b class='flag-5'>定时器</b>

    labview定时器实现实例分享

    labview定时器实现实例分享
    发表于 01-11 09:35 26次下载

    使用555定时器实现延时关灯

    使用555定时器实现延时关灯
    发表于 11-21 14:54 11次下载

    定时器作用及实现定时器数据结构选取介绍1

    定时器在各种场景都需要用到,比如游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等。顾名思义,定时器的主要用途是执行定时
    的头像 发表于 04-21 15:20 1125次阅读
    <b class='flag-5'>定时器</b>作用及<b class='flag-5'>实现</b><b class='flag-5'>定时器</b>数据结构选取介绍1

    定时器作用及实现定时器数据结构选取介绍2

    定时器在各种场景都需要用到,比如游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等。顾名思义,定时器的主要用途是执行定时
    的头像 发表于 04-21 15:20 1136次阅读
    <b class='flag-5'>定时器</b>作用及<b class='flag-5'>实现</b><b class='flag-5'>定时器</b>数据结构选取介绍2

    什么是软件定时器?软件定时器实现原理

    软件定时器是用程序模拟出来的定时器,可以由个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多
    的头像 发表于 05-23 17:05 2632次阅读

    STM32如何使用定时器实现微秒(us)级延时?

    如何使用定时器实现微秒级延时的步骤: 步骤 1:配置定时器 首先,需要选择个适合的定时器。大多数STM32微控制
    的头像 发表于 11-06 11:05 5793次阅读

    定时器设计实现

    返回ITimer类型的共享指针。其中ITimer类中定义了start和stop方法,用于启动或停止当前定时器。 TimerManager还有个内部类TimerMessageQueue用于实现
    的头像 发表于 11-08 16:50 554次阅读

    如何实现个软件定时器

    在Linux,uC/OS,FreeRTOS等操作系统中,都带有软件定时器,原理大同小异。典型的实现方法是:通过个硬件定时器产生固定的时钟节拍,每次硬件
    的头像 发表于 04-29 11:00 550次阅读