8.1 计时器的基本知识

时间:2022-12-24 20:35:20

摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P263

        你可调用 SetTimer 函数为自己的 Windows 程序分配一个计时器。SetTimer 包含一个无符号整型参数,该参数指定了时间间隔的长短,为一毫秒(millisecond)到 4294967295 毫秒(将近 50 天)。这个参数也表示 Windows 给程序发送 WM_TIMER 消息的频率。例如,1000 毫秒就是要求 Windows 每一秒钟向你的程序发送一条 WM_TIMER 消息。

        当程序不在需要计时器时,它可调用 KillTimer 函数停止计时器的消息。在处理 WM_TIMER 消息时调用 KillTimer 函数可以实现一个“一次性”的计时器。KillTimer 函数会清除消息队列中任何没被处理的 WM_TIMER 消息。调用了 KillTimer 之后,程序就不会再收到 WM_TIMER 消息了。

8.1.1  系统和计时器

        Windows 的计时器只是简单地扩展了 PC 硬件和 ROM BIOS 中的计时逻辑。回到 Windows 之前的MS-DOS 编程时代,应用程序的时钟计时器功能是通过截获一个叫“时钟滴答”的 BIOS 中断而实现的。这个中断每 54.925 毫秒出现一次,或每秒出现 18.2 次。这是由原始的 IBM PC 的微处理器时钟频率 4.772720 MHz 被 2^18 整除而得到的。

        Windows 的应用程序并不截获 BIOS 中断。Windows 本身会处理硬件中断,应用程序不需介入。对于当前所设定的每一个计时器,Windows 都会保持一个计数值,硬件时钟滴答每出现一次,这个值会减 1。当计数值减到 0,Windows 会把一个 WM_TIMER 消息放到适当的应用程序的消息队列中,同时把计数值重新设置回它的原始值。

        由于 Windows 应用程序是通过正常的消息队列来接收 WM_TIMER 消息,所以你绝对不用担心程序在处理其他任务时会被突然而来的 WM_TIMER 消息所“中断”。因此计时器与键盘和鼠标相似:驱动程序会处理异步硬件中断事件,Windows 则将这些事件转换为有序的有组织的系列化的消息

        在 Windows 98 上,计时器具有与底层 PC 计时器相同的 55 毫秒的周期。而在 Windows NT 上,计时器的周期大约是 10 毫秒。

        Windows 应用程序不能接收比上述频率更高的 WM_TIMER 消息,也就是说在 Windows 98 上约为 18.2 次,而在 Windows NT 上为每秒 100 次。Windows 会通过 SetTimer 把指定的时间间隔舍入到时钟滴答的整数倍。例如,1000 毫秒时间段被 54.925 除是 18.207 个时钟滴答,它被舍入到 18 个时钟滴答,因此实际的间隔是 989 毫秒。如果时间间隔被设置为少于 55 毫秒,每个时钟滴答都将产生一个 WM_TIMER 消息

8.1.1  计时器消息不是异步的

        因为计时器是基于硬件计时器的中断,程序员有时会被误导,认为他们的程序可能会被异步中断打断后*去处理 WM_TIMER 消息。

        其实 WM_TIMER 消息并不是异步的。WM_TIMER 消息被放在正常的消息队列中,并和其他的消息一同排队等候处理。因此如果你在调用 SetTimer 函数时指定 1000 毫秒,并不能保证程序每秒钟或者每 989 毫秒(如我前面说过的)就收到一个 WM_TIMER 消息。如果你的程序处于忙的状态超过一秒钟,它就会在那个时间内得不到任何 WM_TIMER 消息。本章提供的程序便是证明。事实上,Windows 处理 WM_TIMER 消息和处理 WM_PAINT 消息很类似。这两种消息都是低优先级的,只有当消息队列中没有其他消息时,程序才会收到他们

        WM_TIMER 消息还在另一方面与 WM_PAINT 消息极为类似。Windows 并不会连续不断地产生多个 WM_TIMER 消息到消息队列中。相反,Windows 把在消息队列里的多个 WM_TIMER 消息结合成一条消息。这样,应用程序不会同时收到大量的 WM_TIMER 消息,尽管它可能会收到靠得很近的两个 WM_TIMER 消息。同时,应用程序并不知道有多少这样的 WM_TIMER 消息在这个过程中“丢失”了

        因此一个时钟程序不能通过计算它所收到的 WM_TIMER 消息个数来确认已过去多长时间。WM_TIMER 消息只能告诉应用程序,更新的时间到了。在本章的后面,我们会写两个时钟程序,它们每秒钟更新一次,我们将准确地看到它是如何实现的。

        为方便起见,我将假设计时器每秒钟收到一个 WM_TIMER 消息。但是请记住,这些消息不是精确的时钟滴答中断。