【问题标题】:Is the TTimer.OnTimer event handler reentrant?TTimer.OnTimer 事件处理程序是可重入的吗?
【发布时间】:2011-03-19 16:22:42
【问题描述】:

我的应用程序中有一个 TTimer,它每 2 秒触发一次并调用我的事件处理程序 HandleTimerEvent()。 HandleTimerEvent() 函数修改共享资源,在返回之前可能需要 10 秒的时间来执行。此外,我有时会在事件处理程序中调用 Sleep() 来放弃处理器。

我不确定 C++ builder 的 TTimer 对象在调用事件时是如何工作的,所以我刚才解释的场景让我开始思考,特别是 HandleTimerEvent() 是否在之前的调用返回之前被调用。

问题归结为几件事。

TTimer 对象是否对事件进行排队?

TTimer 对象能否在之前的调用返回之前调用我的事件处理程序?

【问题讨论】:

    标签: delphi c++builder


    【解决方案1】:

    此回复假定 TTimer 仍实现为使用 WM_Timer 消息。如果实施发生了变化(自 2005 年以来),请忽略。

    不,TTimer 对象不对事件进行排队。它由 Windows WM_Timer 消息驱动,Windows 不会让 WM_TIMER 消息在消息队列中堆积。如果出现下一个计时器间隔并且 Windows 发现 WM_Timer 消息已经在应用程序的消息队列中,它不会将另一个 WM_Timer 消息添加到队列中。 (顺便说一句,WM_Paint 也一样)

    是的,即使之前的事件处理程序仍在执行,也有可能触发 TTimer.OnTimer 事件。如果您在事件处理程序中执行任何允许应用程序处理消息的操作,则可以重新输入您的计时器事件。显而易见的是,如果您的事件处理程序调用 Application.ProcessMessages,但它可能比这更微妙 - 如果您在事件处理程序中调用的任何内容在内部调用 Application.ProcessMessages,或调用 PeekMessage/GetMessage + DispatchMessage,或打开一个模式对话框,或调用绑定到进程外 COM 对象的 COM 接口,然后将处理您的应用程序消息队列中的消息,其中可能包括您的下一条 WM_Timer 消息。

    一个简单的解决方案是在您进入计时器事件处理程序时禁用计时器对象,并在您退出计时器事件处理程序时重新启用它。这将防止在您的事件处理程序仍在工作时触发计时器消息,无论您的代码的消息处理特性如何。

    【讨论】:

    • +1 用于禁用计时器。为了演示禁用计时器的有效性(或者简单演示如果不这样做可能会出现什么问题),请在计时器处理程序中显示一个消息框。如果您在进入时不禁用计时器,则消息框会堆积起来。
    • 您也可以使用布尔标志来防止计时器事件处理程序中的重入,但禁用计时器本身要简单得多。
    • 请参阅forums.embarcadero.com/thread.jspa?messageID=171751&#171751 了解有用的 TTimerGuard 类,这是一个用于 TTimer 的 RAII 样式类。可能需要根据您的实施调整 FInterval 的使用。
    【解决方案2】:

    我广泛使用 TTimer。它不对事件进行排队。如果您希望将其移交给事件处理程序,请创建一个 TThread 来处理您的事件,以便 Timer 可以继续它的工作。计时器不是异步操作,而是同步操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多