Я использую очереди таймеров в своем приложении и передаю указатель на один из моих собственных объектов таймера C ++ в качестве «параметра» для обратного вызова (в 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;
...
};
Однако существует тонкое условие гонки: если обратный вызов уже был вызван, но объект таймера уничтожен до вызова TimedOut (), приложение завершает работу, потому что обратный вызов вызывает виртуальный метод на несуществующий объект. Или еще хуже, пока он удаляется.
У меня есть мьютексы для управления многопоточными вызовами, но проблема все еще возникает.
Является ли использование указателя объекта в качестве параметра обратного вызова действительно хорошей идеей? Без каких-либо гарантий синхронизации между потоками он просто пахнет плохо.
Есть ли лучшее решение? Что делают другие люди?
Единственное, что происходит, - это сохранить набор указателей на каждый экземпляр Timer (добавить в конструктор, удалить в деструктор). Но я не думаю, что это сработает, потому что если Timer получен из, мы удалили бы только указатель из набора в деструкторе базового класса; ущерб уже нанесен, если мы начали уничтожать производный объект.
Приветствие.