delphi计时器比计时器服务中断程序更快

delphi timer ticks faster than timer service interrupt routine

嗨我被要求为某人维护一个基于Delphi 5的程序,并且该程序使用一个计时器对象每50毫秒进行一次滴答,并且每次计时它运行单线程代码块。 我只是想知道,如果执行这段代码所花费的时间长于计时器滴答间隔会发生什么,这会不会很糟糕? 例如,它是否会导致访问冲突等问题? Delphi默认如何处理这种情况? 非常感谢。


这个问题的关键部分是:

... what would happen if the time taken to execute this block of code is longer than the timer tick interval, would this be bad?

这不是很好,但它不是一个显示塞子,它肯定不会导致访问违规。 Delphi的TTimer是使用WinAPI SetTimer函数实现的。

您可能天真地认为,如果您的计时器的处理程序花费的时间超过了处理的时间间隔,那么计时器将继续在消息队列中堆积消息,并且您的程序将有效地锁定大量的计时器消息,而这些消息一直没有希望处理。值得庆幸的是,这并不是计时器的工作方式。文档可以解释一些问题。

WM_TIMER消息

The WM_TIMER message is a low-priority message. The GetMessage and PeekMessage functions post this message only when no other higher-priority messages are in the thread's message queue.

现在,在Windows应用程序中并没有真正的"高"和"低"优先级消息的概念,虽然这个语句有点含糊不清,但我们可以将上下文视为wm_Timer是未发布的消息应用程序的消息队列,而是在使用SetTimer设置计时器时,响应GetMessagePeekMessage调用时生成的,当该计时器的时间间隔已经过去,以及队列中已有其他消息时。

因此,虽然在处理程序处理期间可能会经过计时器间隔,但在发生这种情况时进入的任何其他消息仍将正常进入队列,并在处理程序完成后处理。只有在队列再次清空后,才会生成另一条wm_Timer消息。

因此,计时器事件将以滴答间隔的速率执行,或者以应用程序处理它们的速度执行,以最长的为止。但是,如果您确实有过快的定时器消息,并且您的计时器处理程序的处理时间很长,那么您的应用程序的响应能力会受到影响。它不会没有响应,但所有其他消息处理将被限制为在计时器的事件处理程序执行时间间隔内进行处理。这可能会使您的应用程序感到迟钝。

要演示,请创建一个新的表单应用程序,并添加一个间隔设置为10TTimer组件。然后附加此处理程序:

1
2
3
4
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  sleep(200);
end;

程序运行时,尝试移动窗口。我们所做的是基本上将应用程序的消息处理量化为200ms间隔(定时器的事件处理程序执行的持续时间)。


计时器的刻度不会中断您的代码。

定时器滴答以窗口消息的形式提供。窗口消息只能在您检查消息队列中是否有新消息时到达。当您的计时器事件处理程序返回并且程序恢复其事件循环时,这会自动发生,但您可以通过调用Application.ProcessMessages显式触发它。不过,请不要这样说;从长远来看,它很少解决问题。

如果你没有检查你的timer-tick处理程序中的消息队列,那么你的处理程序将永远不会开始运行,而它仍处理上一个tick。

即使你确实检查了队列,所有发生的事情都是递归调用tick处理程序。毕竟,它都在一个线程中运行。然而,递归计时器处理可能不是你想要发生的,所以我将再次建议不要在消息处理程序中检查消息。

此外,如果您的计时器处理程序需要很长时间才能运行,计时器消息永远不会"堆积"。定时器消息是"假的",因为它们实际上不会定期添加到消息队列中。相反,操作系统将在程序检查队列以获取更多消息时合成计时器消息。如果队列中没有优先级较高的消息,并且计时器间隔已过,则OS将返回wm_Timer消息。如果不检查更多消息,则队列中将没有计时器消息。特别是,队列中不会有多个计时器消息。

进一步阅读:

  • Stack Overflow:消息队列在Win32中如何工作?
  • Dobbs博士:Windows消息系统内部