CreateTimerQueueTimer回调和竞争条件

Ste*_*lly 5 c++ winapi timer

我正在我的应用程序中使用计时器队列,并将指针传递给我自己的一个C++ Timer对象作为回调的"参数"(在CreateTimerQueueTimer中).然后我在回调中的对象上调用虚方法.

Timer对象的析构函数将确保使用DeleteTimerQueueTimer()取消计时器.

static void callback( PVOID param, BOOLEAN timerOrWaitFired )
{
    Timer* timer = reinterpret_cast< Timer* >( param );
    timer->TimedOut();
}

class Timer
{
public:
   Timer();

   virtual ~Timer()
   {
       ::DeleteTimerQueueTimer( handle );
   }

   void Start( double period )
   {
      ::CreateTimerQueueTimer( &handle, ..., &callback, this, ... );
   }

   virtual void TimedOut() = 0;

   ...
};
Run Code Online (Sandbox Code Playgroud)

但是,有一个微妙的竞争条件,如果已经调用了回调,但是在调用TimedOut()之前销毁了计时器对象,则应用程序崩溃,因为回调调用不存在的对象上的虚方法.或者更糟糕的是,它被删除了.

我确实有互斥锁来控制多线程调用,但我仍然遇到问题.

使用对象指针作为回调参数真的是个好主意吗?由于不保证线程之间的同步,它对我来说闻起来很糟糕.

有更好的解决方案吗?其他人做什么?

发生的一件事是保留一组指向每个Timer实例的指针(添加构造函数,在析构函数中删除).但我认为这不会起作用,因为如果从中派生出Timer,我们只会从基类析构函数中的集合中删除指针; 如果我们已经开始销毁派生对象,则已经完成了损坏.

干杯.

jpa*_*cek 3

使用对象指针作为回调函数参数的概念本身并不错。但是,显然您需要在最后一个回调退出后开始销毁。

所以,我根本不会让 Timer 抽象并从中派生。我将使用另一个抽象类TimerImpl并使该类Timer使用一个TimerImpl实例:

class Timer
{
  TimerInstance* impl;
  void TimeOut() { impl->TimeOut(); }
public:
  ~Timer() {
    ... make sure the timer has ended and wont fire again after this line...
    delete impl;
  }
}

struct TimerImpl
{
  virtual void TimeOut()=0;
  virtual ~TimerImpl();
}
Run Code Online (Sandbox Code Playgroud)

这样,您就可以确保破坏不会在您说完之后开始。

第二件事是,你必须等待最后一个计时器事件烧完。根据MSDN 文档,您可以通过调用来完成

DeleteTimerQueueTimer(TimerQueue, Timer, INVALID_HANDLE_VALUE)
Run Code Online (Sandbox Code Playgroud)