首页 > 程序人生 > 定时器(Timer)的实现

定时器(Timer)的实现

本文是继前面写过的《谈事件驱动模型中的“超时”(Timeout)处理》的第二篇,主要谈一谈事件驱动模型超时处理中用到的定时器的实现,以及如何用定时器来处理事件驱动模型中的超时事件。

下面就从以下几个方面谈一下定时器:
1. 什么是定时器?
定时器,又称Timer,顾名思义,它是用来在指定的时刻完成特定任务的一种工具的抽象。

2. 定时器是干什么用的?
在程序中,定时器常被用来完成两类任务,一种是周期性的任务,就是每隔指定的时间执行一次的任务,另一种是在特定时刻要执行的任务,就是只在某一个特定的时刻执行,而且只执行一次的任务。
举个生活中的例子,闹钟,相信大家都不陌生,比如我们定了每天早上八点起床的闹钟,然后它就会在每天八点这个特定的时刻执行响铃这个任务,来叫我们起床,这就是上面讲到的第一种类型的定时器任务。再一个例子,就是日历(用过谷歌日历的读者朋友应该知道),我们在谷歌日历中制定一段时间的行程安排,它在每天那个特定的时刻就会提醒我们做这一时刻该做的事,这就是上面讲的第二种类型的定时器任务。

3.如何实现一个定时器?
通常,在程序中,一个定时器都是一个独立的线程,这个线程专门来处理定时器事件。需要注意两点,一是在定时器中,最好不要执行可阻塞的事件,如果定时器被阻塞了,后面的事件就不能被按时处理了。二是在定时器中,最好不要执行太抢占CPU的事件,如果定时器很抢CPU,就会导致应用程序性能下降。总结一下,定时器有以下几个要素:
(1)独立的线程,与应用程序互不影响
(2)任务不阻塞,否则会导致后续定时器任务不能按时处理
(3)任务不能太抢CPU,否则会降低应用程序性能

4. 如何用定时器处理事件驱动模型中的超时事件?
在这里,我就不绕弯子了,直接讲解决方案。我们用一个以时间为key的最小堆来存储所有的超时事件的上下文信息(Context)。比如说事件A的超时时间是30秒,当前时间是1269271046,那么,事件A的超时事件上下文(Context)的key就是30+1269271046=1269271076,我们把事件A的超时事件上下文(Context)以1269271076为key,插入到上面提到的最小堆中。我们的定时器周期性的来检查堆顶的任务的key,如果该key比当前时间大,那么说明该事件还未超时,而且所有的在堆里的事件都未超时(这里是为什么?希望留给读书者朋友一个小小的问题 🙂 );如果该key比当前时间小,就认为该事件已经超时了,然后把这个事件上下文从堆中弹出堆,发消息通知应用层的事件处理器处理超时事件,然后继续检查堆顶的事件,直到检查到没有超时的事件为止。
当然,上面只是我的一种实现,肯定还有更好的方法,希望感兴趣的朋友可以分享一下 😛

以上讲的所有内容,都是理论层面的,具体的代码实现细节,在这里我就不贴了,我想只要原理清楚了,实现起来应该是a piece of cake!!!

  1. 2010年3月23日14:30 | #1

    唉,技术不行,看不大懂。顶一下。

  2. 2010年3月23日20:51 | #2

    @老实人博客 呵呵,术业有志攻而已,你懂的我未必能懂~

  3. Will
    2010年11月26日05:57 | #3

    “我们的定时器周期性的来检查堆顶的任务的key” – 所谓周期性,你用多大的周期来扫你的mini heap?

    过频繁影响性能,不频繁会造成“不即时”的反馈

    • 2010年11月26日09:18 | #4

      1. 这个周期是可调的,1毫秒差不多了,再小了不准
      2. 可以用单独一个线程,不会影响其它的性能,最坏的情况就是多占一个cpu核

  1. 2010年4月18日12:34 | #1
您必须在 登录 后才能发布评论.