当前位置:首页 > 消费电子 > 消费电子
[导读]对于一个游戏而言,定时器是必须的,而它一般作为一个游戏基本公共组件,而定时器在游戏逻辑中运用是非常明显的(比如吃药回血,每几秒回血多少),而对于游戏逻辑而言需要开

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

一:分析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

12 CLinkList( int size );

13

14 ~CLinkList(void);

15

16 /**////

17 /// 初始化

18 ///

19 void init();

20

21 /**////

22 /// 让指针指向自己

23 ///

24 void init_list_self( int index);

25

26 /**////

27 /// 把news插入到prev,next之间

28 ///

29 void insert_listnode(ListNode *news , ListNode* prev , ListNode* next);

30

31 /**////

32 /// 插入到链表头

33 ///

34 void insert_head( ListNode* news , ListNode* head );

35

36 /**////

37 /// 插入到链表尾

38 ///

39 void insert_tail( ListNode* news , ListNode* head );

40

41 /**////

42 /// 删除节点

43 ///

44 void list_del( ListNode* list);

45

46 /**////

47 /// 得到改轮子转到第几个插槽

48 ///

49 int GetIndex( ) const { return m_index ;}

50

51 /**////

52 /// 更新轮子的插槽

53 ///

54 void SetIndex(int idx) { m_index = idx ;}

55

56 /**////

57 /// 得到轮子插槽的指针

58 ///

59 ListNode* GetNode(int index) const;

60

61private:

62 /**////

63 /// 轮子的插槽数组

64 ///

65 ListNode *m_List;

66

67 /**////

68 /// 轮子当前转到的索引

69 ///

70 int m_index;

71

72 /**////

73 /// 轮子大小

74 ///

75 int m_Size;

76

77};[!--empirenews.page--]

4)定时器管理类

定时器管理类

1/**////

2/// 定时器管理类,管理定时器的五个轮子

3///

4class CTimer

5{

6public:

7 /**////

8 /// 构造函数如下

9 ///

10 CTimer(void);

11

12 CTimer( int second);

13

14 ~CTimer(void);

15

16public:

17 /**////

18 /// 初始化定时器管理类

19 ///

20 void Init(int Second = 0);

21

22 /**////

23 /// 增加一个定时器

24 ///

25 void add_timer(timernode *times );

26

27 /**////

28 /// 检测定时器是否存在

29 ///

30 /// @return 如果存在返回true,否则为false

31 ///

32 bool check_timer(timernode* times);

33

34 /**////

35 /// 删除定时器

36 ///

37 /// @return 如果删除成功返回true,否则为false

38 ///

39 bool delete_timer(CLinkList* list, timernode *times);

40

41 /**////

42 /// 重新初始化一个定时器

43 ///

44 void init_timer(timernode* timers);

45

46 /**////

47 /// 定时器的迁移,也即将一个定时器从它原来所处的定时器向量迁移到另一个定时器向量中。

48 ///

49 void cascade_timer(CLinkList* timers);

50

51 /**////

52 /// 执行当前已经到期的定时器,所有小于jeffies的定时器

53 ///

54 void Expires( ulong jeffies);

55

56 /**////

57 /// 重新初始化一个定时器

58 ///

59 void Cancel(timernode* timers);

60

61 /**////

62 /// 重新计算一个定时器

63 ///

64 void Mod_timer(timernode* timers);

65

66private:

67 /**//// 5个轮子

68 CLinkList* m_tv1;

69 CLinkList* m_tv2;

70 CLinkList* m_tv3;

71 CLinkList* m_tv4;

72 CLinkList* m_tv5;

73 CLinkList** g_vecs;

74

75 /**//// 定时器全局tick

76 ulong m_jeffies;

77 /**//// 上次运行时间

78 ulong m_Lasttime;

79 /**//// 精确到毫秒

80 ulong m_mSecond;

81};

82

四: 测试

通过本文的介绍可以理解次定时器的的查找,删除,插入都是O(1)的复杂度。

/**//// 游戏事件基类

class CGameEvent

{

public:

virtual long TimeOut( eTimerType type) = 0;

};

测试例子:

1long Sum1= 0 ;

2long Sum2= 0 ;

3long Sum3= 0 ;

4long Sum = 0;

5

6class CTimertest : public CGameEvent

7{

8public:

9 long TimeOut( eTimerType type)

10 {

11 switch ( type)

12 {

13 case eTimer1:

14 std::cout <<"Sum1 = "<< Sum1 ++ << std::endl;

15 break;

16 case eTimer2:

17 std::cout <<"Sum2 = "<< Sum2 ++ << std::endl;

18 break;

19 case eTimer3:

20 std::cout <<"Sum3 = "<< Sum3 ++ << std::endl;

21 break;

22 default:

23 std::cout <<"Sum = "<< Sum ++ << std::endl;

24 break;

25 }

26 return 0;

27 }

28};

29

30int _tmain(int argc, _TCHAR* argv[])

31{

32 CTimer mytimer( 40 );

33

34 long n;

35 std::cin >> n;

36 CTimerTest test;

37 for ( int i = 0 ; i < n ; i++ )

38 {

39 timernode* tim = new timernode ;

40 tim->expires = 0;

41 tim->etime = GetCurrSystemTime() + (rand() % 1000 ) * 6;

42 tim->pFun =&test;

43 tim->eType =(eTimerType)( i%3 + 10 );

44

45 mytimer.add_timer( tim );

46 }

47

48 for ( ;; )

49 {

50 if ( (Sum1 + Sum2 + Sum3) == n )

51 break;

52

53 /**//// 运行所有的定时器

54 mytimer.Expires( GetCurrSystemTime() );

55 }

56

57 std::cout << " sum1 = " << Sum1

58 << " sum2 = " << Sum2

59 << " sum3 = " << Sum3

60 << " sum = " << Sum ;

61 return 0;

62}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭