1

间隔设置为 1 秒的 TTimer 每 1 秒发送一条消息。此消息在应用程序的消息循环中进行处理,从而触发 OnTimer 事件。
如果应用程序很忙并且没有时间处理消息循环,则跳过 OnTimer 事件。

我知道 TTimer 在内部使用SetTimer

我的问题是:

  1. TTimer 是否使用内部/单独的线程(通过 SetTimer)?
  2. 如果模态 MessageDlg 正在“阻止”表单,那么保存计时器(甚至它的 OnTimer)的表单为什么仍然可以做事呢?(见下面的代码)
  3. 文档说 SetTimer 至少需要 Win2000。TTimer在Win98中是如何实现的?

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
 Caption = i;
 i++;
 MessageDlg(stuff);      <----- we "block" application here but form's caption is still updated.
}
4

2 回答 2

7

如果应用程序很忙并且没有时间处理消息循环,则跳过 OnTimer 事件。

这实际上是正确的。这个和MSDN 上的这篇博客文章提供了一些内部实现细节,特别是他们提到一个过期的计时器会导致QS_TIMER设置消息队列状态的标志。没有进一步的时间流逝将导致队列状态标志被设置得更多。当此标志被设置并且[Peek|Get]Message无法选择任何更高优先级的消息时,将生成一个计时器消息。

尽管计时器消息不会堆积在队列中,但可以在执行前一个事件处理程序时再次触发计时器。当处理程序中的代码执行时间超过计时器间隔并且允许重新进入时,这是可能的。如果计时器处理程序使应用程序处理排队的消息,则可能会清除任何未决队列状态标志并再次发布消息,这可能会导致计时器在处理程序完成执行之前触发。

TTimer 是否使用内部/单独的线程(通过 SetTimer)?

不会。在主线程中创建一个实用程序窗口,它将接收计时器消息。在收到计时器消息后,如果已分配事件处理程序,则此窗口将调用事件处理程序。

如果模态 MessageDlg 正在“阻止”表单,那么保存计时器(甚至它的 OnTimer)的表单为什么仍然可以做事呢?

模态循环继续处理队列,它HandleMessage在循环中调用应用程序,该循环调用ProcessMessage. 因此定时器消息仍然被处理。

这是上述重入的潜在原因。您可以使用标志或禁用/启用计时器来防止这种情况。或者完全排除处理程序中的任何消息处理。

文档说 SetTimer 至少需要 Win2000。Win98中TTimer是如何实现的

相同的。文档不断变化,有时 MSDN 会从最低要求中删除不受支持的操作系统版本 - 相当不一致。我的 XE2 API 文档指出:

最低操作系统Windows 95、Windows NT 3.1

WM_TIMER消息。

于 2018-09-26T15:18:14.277 回答
4

WM_TIMER 消息永远不会放在消息队列中。它们是在队列为空并且设置了指示计时器已过期的标志时生成的。因此,队列中一次永远不会有超过一条 WM_TIMER 消息,如果您的应用程序太忙而无法处理队列,您不会得到大量等待处理的 WM_TIMER 消息。

WM_PAINT 消息的工作方式相同。

于 2018-09-26T11:53:38.330 回答