Introduction
There are a wide variety of Windows timers available, this along with their different methods of implementation can make the task of including a simple timer into a piece of source code rather confusing, especially for someone new to C++. TemplateTimer
does noting special but offers a convenient wrapper for the programmer who needs to implement a simple timer within a C++ class.
Code Discussion
In this implementation, CreateTimerQueueTimer()
has been used as the timer but there are several different timers available. CreateTimerQueueTimer()
has been implemented using WT_EXECUTEINTIMERTHREAD
(for short tasks) but may be changed to suit specific requirements. It should be noted that this timer (as with all Windows timers) suffers from inherent non real-time OS inaccuracies. The performance is system dependent; however, after testing, the timer resolution was accurate around and above a 200ms interval.
The Start()
method has two overloads. The default method simply allows the timer to be started with an interval in milliseconds. The first overload allows the timer to be started immediately (i.e., the first timed event is called immediately). The second overload allows the timer to be called only once.
The templated class TTimer
implements a callback using templates. This allows the specification of a class function as the callback function through SetTimedEvent()
.
CTimer
implements a virtual method OnTimedEvent()
that is called by the timer procedure TimerProc()
. TTimer
derives from CTimer
, and overrides the OnTimerEvent()
method which in turn calls the (user defined) callback function.
GetCount()
and SetCount()
use InterlockedExchangeAdd()
and InterlockedExchange()
to provide a thread safe timer event count that can be read (or set) anywhere in the code. This use of InterlockedExchange()
provides a convenient way of providing very quick mutex access to a number.
The Stop()
method is used to stop the timer. If required, it can be used within the OnTimedEvent()
function, for example to stop timer1
after 10 times (10 sec) use: if( timer1.GetCount() == 10 ){ timer1.Stop(); }
.
Using the Code
For convenience, the TTimer
and CTimer
classes have been implemented in a single file (TemplateTimer.h). To use TemplateTimer
, simply include TemplateTimer.h into your project and implement as shown in the test code TimerTest.cpp/.h.
'TemplateTimer.h' (see the 'Download source' link above)
#pragma once
#include <atlbase.h>
static void CALLBACK TimerProc(void*, BOOLEAN);
class CTimer
{
public:
CTimer()
{
m_hTimer = NULL;
m_mutexCount = 0;
}
virtual ~CTimer()
{
Stop();
}
bool Start(unsigned int interval, bool immediately = false, bool once = false) {
if( m_hTimer )
{
return false;
}
SetCount(0);
BOOL success = CreateTimerQueueTimer( &m_hTimer,
NULL,
TimerProc,
this,
immediately ? 0 : interval,
once ? 0 : interval,
WT_EXECUTEINTIMERTHREAD);
return( success != 0 );
}
void Stop()
{
DeleteTimerQueueTimer( NULL, m_hTimer, NULL );
m_hTimer = NULL ;
}
virtual void OnTimedEvent()
{
}
void SetCount(int value)
{
InterlockedExchange( &m_mutexCount, value );
}
int GetCount()
{
return InterlockedExchangeAdd( &m_mutexCount, 0 );
}
private:
HANDLE m_hTimer;
long m_mutexCount;
};
void CALLBACK TimerProc(void* param, BOOLEAN timerCalled)
{
CTimer* timer = static_cast<CTimer*>(param);
timer->SetCount( timer->GetCount()+1 );
timer->OnTimedEvent();
};
template <class T> class TTimer : public CTimer
{
public:
typedef private void (T::*TimedFunction)(void);
TTimer()
{
m_pTimedFunction = NULL;
m_pClass = NULL;
}
void SetTimedEvent(T *pClass, TimedFunction pFunc)
{
m_pClass = pClass;
m_pTimedFunction = pFunc;
}
protected:
void OnTimedEvent()
{
if (m_pTimedFunction && m_pClass)
{
(m_pClass->*m_pTimedFunction)();
}
}
private:
T *m_pClass;
TimedFunction m_pTimedFunction;
};
Here is the example code (see the 'Download demo project' link above).
TimerTest.h
#pragma once
#include "TemplateTimer.h"
class CTimerTest
{
public:
void RunTest();
private:
void OnTimedEvent1();
void OnTimedEvent2();
TTimer<CTimerTest> timer1 ;
TTimer<CTimerTest> timer2 ;
};
TimerTest.cpp
#include "stdafx.h"
#include "TimerTest.h"
void CTimerTest::OnTimedEvent1()
{
printf("\r\nTimer 1 Called (count=%i)", timer1.GetCount());
}
void CTimerTest::OnTimedEvent2()
{
printf("\r\nTimer 2 Called (count=%i)", timer2.GetCount());
}
void CTimerTest::RunTest()
{
printf("Hit return to start and stop timers");
getchar();
timer1.SetTimedEvent(this, &CTimerTest::OnTimedEvent1);
timer1.Start(1000);
timer2.SetTimedEvent(this, &CTimerTest::OnTimedEvent2);
timer2.Start(2000);
getchar();
timer1.Stop(); timer2.Stop();
printf("\r\nTimers stopped (hit return to exit)");
getchar();
}
Example output