delphi定时器比定时器服务中断例程运行得更快



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

这个问题的关键部分是:

。。。如果执行这段代码所花费的时间比计时器的时间间隔长,会发生什么,这会很糟糕吗?

这不太好,但它不是一个阻止,而且它肯定不会导致访问违规。Delphi的TTimer是使用WinAPISetTimer函数实现的。

你可能会天真地认为,如果你的计时器的处理程序处理的时间比间隔长,那么计时器会继续在消息队列中堆积消息,你的程序会被大量的计时器消息有效地锁定,而这些消息根本不可能被处理完。值得庆幸的是,计时器并不是这样工作的。这些文件可以提供一些启示。

WM_TIMER消息

WM_TIMER消息是一条低优先级消息。只有当线程的消息队列中没有其他优先级更高的消息时,GetMessage和PeekMessage函数才会发布此消息。

现在,在windows应用程序中并没有真正的"高"one_answers"低"优先级消息的概念,虽然这句话有点模棱两可,但我们可以将上下文视为WM_TIMER是一条消息,它不会发布到应用程序的消息队列,而是在使用SetTimer设置计时器时响应GetMessagePeekMessage调用而生成,当该计时器的间隔已过,并且队列中没有其他消息时。

因此,虽然在处理程序的处理过程中计时器间隔可能会过去,但在处理程序完成时,任何其他传入的消息仍将正常进入队列,并将被处理。只有队列再次清空后,才会生成另一条WM_TIMER消息。

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


示例

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

procedure TForm1.Timer1Timer(Sender: TObject);
begin
sleep(200);
end;

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

计时器的滴答声不会中断代码。

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

如果您不在计时器刻度处理程序中检查消息队列,那么当您的处理程序仍在处理前一个刻度时,它将永远不会开始第二次运行。

即使确实检查了队列,也只会递归地调用tick处理程序。毕竟,这一切都在一个线程中运行。不过,递归定时器处理可能不是您想要的,所以我将再次建议不要在消息处理程序中检查消息。

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

进一步阅读:

  • 堆栈溢出:消息队列在Win32中是如何工作的
  • Dr。多布斯:Windows消息系统内部

最新更新