2010-07-22 30 views
18

我在我的应用程序中有一个TTimer,每2秒触发一次并调用我的事件处理程序HandleTimerEvent()。 HandleTimerEvent()函数修改共享资源,并且在返回之前可能需要10秒才能执行。此外,我在事件处理程序中调用Sleep()以便有时放弃处理器。TTimer.OnTimer事件处理程序是否可重入?

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

这个问题归结为几件事情。

TTimer对象是否排队事件?

在先前调用返回之前,TTimer对象可以调用我的事件处理程序吗?

回答

31

此回复假定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消息。

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

+10

+1用于禁用定时器。为了演示禁用定时器的有效性(或简单演示如果不这样做会出错),请在定时器处理程序中显示一个消息框。如果您在进入时不禁用定时器,则消息框将叠加起来。 – 2010-07-22 18:26:44

+2

您也可以使用布尔标志来防止定时器事件处理程序中的重入,但禁用定时器本身要简单得多。 – dthorpe 2010-07-22 21:23:21

+0

请参阅https://forums.embarcadero.com/thread.jspa?messageID=171751𩻧以获取有用的TTimerGuard类,TTimer使用的RAII风格类。可能需要根据您的实施情况调整FInterval使用情况。 – 2011-01-27 17:04:50

2

我广泛使用TTimer。它不排队事件。如果你想把它交给事件处理程序,那么创建一个TThread来处理你的事件,这样Timer就可以继续工作。定时器不是异步操作,而是同步操作。

相关问题