一个高效的定时器分析及设计-设计应用_第1页
一个高效的定时器分析及设计-设计应用_第2页
一个高效的定时器分析及设计-设计应用_第3页
一个高效的定时器分析及设计-设计应用_第4页
一个高效的定时器分析及设计-设计应用_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

一:分析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)常量定义

/**////definem

#definelnum5

#definesbits6

#defineebits8

#definesbitsize(1sbits)

#defineebitsize(1ebits)

#definesMask(sbitsize-1)

#defineeMask(ebitsize-1)

2)数据结构

1/**////定时器指针结点

2structListNode

3{

4ListNode*next,*prev;

5};

6

7/**////

8///定时器类型

9///

10enumeTimerType

11{

12eTimer1=10,

13eTimer2,

14eTimer3

15};

16

17/**////

18///定时器结点,tlist表示结点的指针,expires循环周期时间

19///etime触发周期时间,pFun触发函数.

20///

21structtimernode

22{

23ListNodetlist;

24ulongexpires;

25ulongetime;

26void*pFun;

27eTimerTypeeType;

28};

3)轮子类

1/**////

2///一个轮子,一个循环队列

3///

4///

5classCLinkList

6{

7

8public:

9

10CLinkList(void);

11

12CLinkList(intsize);

13

14~CLinkList(void);

15

16/**////

17///初始化

18///

19voidinit();

20

21/**////

22///让指针指向自己

23///

24voidinit_list_self(intindex);

25

26/**////

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

28///

29voidinsert_listnode(ListNode*news,ListNode*prev,ListNode*next);

30

31/**////

32///插入到链表头

33///

34voidinsert_head(ListNode*news,ListNode*head);

35

36/**////

37///插入到链表尾

38///

39voidinsert_tail(ListNode*news,ListNode*head);

40

41/**////

42///删除节点

43///

44voidlist_del(ListNode*list);

45

46/**////

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

48///

49intGetIndex()const{returnm_index;}

50

51/**////

52///更新轮子的插槽

53///

54voidSetIndex(intidx){m_index=idx;}

55

56/**////

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

58///

59ListNode*GetNode(intindex)const;

60

61private:

62/**////

63///轮子的插槽数组

64///

65ListNode*m_List;

66

67/**////

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

69///

70intm_index;

71

72/**////

73///轮子大小

74///

75intm_Size;

76

77};

4)定时器管理类

定时器管理类

1/**////

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

3///

4classCTimer

5{

6public:

7/**////

8///构造函数如下

9///

10CTimer(void);

11

12CTimer(intsecond);

13

14~CTimer(void);

15

16public:

17/**////

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

19///

20voidInit(intSecond=0);

21

22/**////

23///增加一个定时器

24///

25voidadd_timer(timernode*times);

26

27/**////

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

29///

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

31///

32boolcheck_timer(timernode*times);

33

34/**////

35///删除定时器

36///

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

38///

39booldelete_timer(CLinkList*list,timernode*times);

40

41/**////

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

43///

44voidinit_timer(timernode*timers);

45

46/**////

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

48///

49voidcascade_timer(CLinkList*timers);

50

51/**////

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

53///

54voidExpires(ulongjeffies);

55

56/**////

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

58///

59voidCancel(timernode*timers);

60

61/**////

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

63///

64voidMod_timer(timernode*timers);

65

66private:

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

68CLinkList*m_tv1;

69CLinkList*m_tv2;

70CLinkList*m_tv3;

71CLinkList*m_tv4;

72CLinkList*m_tv5;

73CLinkList**g_vecs;

74

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

76ulongm_jeffies;

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

78ulongm_Lasttime;

79/**////到毫秒

80ulongm_mSecond;

81};

82

四:测试

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

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

classCGameEvent

{

public:

virtuallongTimeOut(eTimerTypetype)=0;

};

测试例子:

1longSum1=0;

2longSum2=0;

3longSum3=0;

4longSum=0;

5

6classCTimerTest:publicCGameEvent

7{

8public:

9longTimeOut(eTimerTypetype)

10{

11switch(type)

12{

13caseeTimer1:

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

15break;

16caseeTimer2:

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

18break;

19caseeTimer3:

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

21break;

22default:

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

24break;

25}

26return0;

27}

28};

29

30int_tmain(intargc,_TCHAR*argv[])

3

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论