一个高效的定时器分析及设计

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效 阅读详情

       对于一个游戏而言,定时器是必须的,而它一般作为一个游戏基本公共组件,而定时器在游戏逻辑中运用是非常明显的(比如吃药回血,每几秒回血多少),而对于游戏逻辑而言需要开发一个高效率高精度(毫秒级别)的定时器。

    
一:分析Ace库定时器实现方式

   1.Ace种定时器实现有4种,这里不具体介绍实现细节,主要介绍实现数据结构,性能。
       具体的4种定时器都是从ACE_Timer_Queue_T继承,每种定时器用不同的数据结构来实现具体Timer的算法。
      1)ACE_Timer_Heap定时器,根据触发时间建立一个优先级队列(一个最小堆数据结构)来维护所有的定时器,代价就是删除和插入过程为O(logn),代价比较高。
      2)ACE_Timer_List定时器,根据触发时间建立一个有序的双向链表,代价就是插入定时器代价较高。
      3)ACE_Timer_Hash定时器,采用开链的Hash方式每一个桶为一个单链表,在检查所有桶超时的时候会遍历链表所有的元素。为了提高效率这里所用的Hash桶应该足够 大,而对于定时器一般是频繁的超时响应定时器,已经插入和删除,响应会采用迭代的方式。所以效率并不是那么高效。
      4)ACE_Timer_Wheel定时器,采用的一种时间轮的方式,具体实现就好象一个轮子上面有很多插槽,每一个插槽下面包括一个有序双向链表,在Ace中把轮子叫做Wheel,插槽叫做Spoke,每一个定时器被Hash到Spoke中,而Spoke也可以理解为timer的分辨率,而Spoke的计算公式为 :( 触发时间 >> 分辨率的位数)&(spoke大小-1).然后在根据触发时间把定时器插入到每一个Spoke的有序双向链表中, 与Ace_timer_Hash的实现类似,只是这里用户可以指定Spoke大小。这里代价就是插入的时候可能最坏为O(n).
    
      我们公司现在CTimer就是采用Ace的ACE_Timer_Wheel原理设计的
      这里有一个图更能直观的描述这种思想:
    
           实现方式为Vector,list组合。


 二: 本文介绍一种采用linux中断处理的定时器设计方式  
     此定时器的查找,删除,插入都是O(1)
     1) 介绍设计原理
     定时器是基于时间的中断函数,即是根据触发时间来超时响应。所以只要我们设计一个基于时间的Hash算法。只要我们能我们把一个函数触发时间全部Hash到此Hash算法的桶中,就实现了查找,插入,删除O(1)的操作了,其实不然基于时间的hash算法好像挺复杂,而且桶的数量太大,内存消耗太多,所以把一个时间直接Hash代价太大。是否有一种其他的方式呢,linux中断处理采用一种类似水表计算水量的方式,方式就是生活中的水表,第一个指针转一圈后一个转一格,假设每一个圈都是10个刻度,第一个圈能表示10,那么第二圈没一个刻度表示第一个圈的1圈,就能表示10*10, 二个圈一共就能表示10*10 + 10。 以此类推,5个圈就能表示10^5+10^4+10^3+10^2+10...

      一个32bits的整数,如果精确到1毫秒,则2^32位可以表示49.3天,而一般服务器应该不会直接运行49.3天,这里我们采用5个轮子(即圈), 轮子大小分别为256,64,64,64,64 ,轮子依次类推表示范围在0~256, 256~256*64, 256*64~256*64^2, 256*64^2~256*64^3, 256*64^3~256*64^4, 假设这里精度为n毫秒,第一个轮子表示n*256秒时间内触发函数,第二个轮子的第二个插孔则表示n*256*2时间范围内的,

    2)一些定义:
           A. 轮子,这里采用的轮子与上面介绍的Ace轮子大概一样,一个循环列队,每一个插槽你们有一个双向链表,注意这里链表不需要排序,所以在插入的是O(1)的操作。轮子为5个。

    3)  操作:
           A. Hash算法:这里Hash算法根据他的多少时间后触发,直接Hash得到轮子以及插槽,然后插入到某个插槽双向的链表中。
           B.定时器触发: 定时器触发只会触发第一个轮子超时的所有定时器,因为后面4个轮子定时器表示都在前1轮子触发完了才会触发,所以这里让后面4个轮子维护表示将要发生的定时。这里会根据当第一个轮子转第几圈后,第二个轮子会把第几插槽的所有定时器全部插入到第一个轮子中,依次类推,第二个轮子转一个第三个轮子某个插槽又会插入到第二个轮子中。。。
           
   4)注意的地方:
          A.将一个定时器插入到它应该所处的定时器轮子插槽中。
          B.定时器的迁移,也即将一个定时器从它原来所处的轮子插槽迁移到另一个轮子插槽中。
          C.超时响应执行当前已经到期的定时器。

三:编码实现
         1) 常量定义
                   

/// define m
#define lnum        5
#define sbits        6
#define ebits        8
#define sbitsize    ( 1 << sbits )
#define ebitsize    ( 1 << ebits )
#define sMask        ( sbitsize- 1)
#define eMask        ( ebitsize -1)


         2) 数据结构
                 

 1/// 定时器指针结点
 2struct ListNode
 3{
 4    ListNode *next,*prev;
 5}
;
 6
 7///
 8/// 定时器类型
 9/// 

10enum eTimerType
11{
12    eTimer1 = 10,
13    eTimer2 ,
14    eTimer3 
15}
;
16
17/// 
18/// 定时器结点,tlist表示结点的指针,expires循环周期时间
19/// etime 触发周期时间,pFun触发函数.
20/// 

21struct timernode
22{
23    ListNode    tlist;
24    ulong       expires;
25    ulong       etime;
26    void        *pFun;
27    eTimerType  eType;
28}
;

  3) 轮子类
                     

 1///              
 2/// 一个轮子,一个循环队列
 3/// 
 4/// 

 5class CLinkList
 6{
 7
 8public:
 9
10    CLinkList(void);
11
基于ACE中ACE_Event_Handler的定时器管理器实现 在C++项目开发中有许多用到定时器的地方,比如各个进程间定时发心跳数据,ftp服务定时扫描文件等等。由于定时器实现一般属于框架类的东西,工作中如果业务做的比较多的话容易忘记,这里记录一下基于ACE的定时器管理器的实现。 【定时器类】 1、由于是要被管理的,所以每个定时器都需要有一个定时器Id; 2、handle_timeout函数为定时器到点时候调用的函数; 3、设置定时器ID; 4、获取定 阅读详情

相关推荐

一种高效率的定时器管理模块设计

引  言   定时器是通信协议正常运行的基本要素之一,主要用于各种定时帧重传的任务。通信协议在单片机系统上实现所使用的定时器,定时精度要求不高,但数量要求比较大。由于硬件资源有限,不可能为每一个单独任务分配一个硬件定时器,只能通过单个硬件定时器模拟多个软件定时器的方法,来满足协议中的定时应用需要。   用一定的数据结构将这些软件定时器组织起来,提供统一的调用接口,称为“定时器管理”。目前定时器管理主要有2种实现方法:   ①静态数组法。将定时器节点存储在数组中。优点是逻辑简单,占用ROM较少。但这种方案有明显的缺点:当硬件定时器中断发生时,要对所有定时器节点进行减法操作,时间开销很大

C++ 定时器任务的多种实现方法详解

在现代编程中,定时器任务Timer Task)是一项常见且重要的功能。无论是在嵌入式系统、网络应用,还是桌面软件开发中,定时器任务都能帮助我们在指定时间点或时间间隔内执行特定操作。本文将详细介绍几种在 C++ 中实现单次定时器任务的方法,帮助开发者在不同的应用场景中选择合适的解决方案。

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 3560

实现一个定时任务,可以用什么数据结构算法

通过小顶堆,可以高效地找到最近触发的定时事件,在触发时执行相应的任务。3、链表(用的比较少):链表可以用于管理定时事件,每个节点包含触发时间戳任务信息。链表中的节点按照触发时间戳从小到大排列,通过遍历链表,可以找到最近触发的定时事件执行任务。它将时间划分成若干个时间槽,使用循环队列来存储在每个时间槽上触发的任务,从而避免了遍历整个定时事件集合的开销。定时任务,是我们经常要用的,不管是)ava中内置的,还是一些框架中的。如Java中的Timer、DelayQueue等,都是基于小顶堆实现的。

weixin_51291483的博客 367

网络游戏中的定时器设计

网络游戏中的定时器设计    最近在开发的网络游戏中尝试引入定时器的功能,比较几种常用的定时器算法之后,选择了Linux 的内核的定时器算法:时间轮算法。(Linux 的内核把定时器分为 5 组,每组的粒度分别表示为:1 jiffies,256 jiffies,256*64 jiffies,256*64*64 jiffies,256*64*64*64 jiffies,每组中桶的数量分别为:256,64,64,64,64,这样,在 256+64+64+64+64 = 512 个桶中,表示的范围为 2^32 )

dijinzhou的专栏 2962
上一篇: 第3章 Data语意学-《深度探索C++对象模型》读书笔记
worldvacuum
博客等级 码龄17年 0粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值