一、定时器的用途
定时器在各种通信中有重要的用途。
二、定时器要实现的功能
根据指定的时间间隔产生定时器事件,并达到指定的精度。精度一般由定时器的采样精度相关,也就是和定时器硬件有关。在普通PC中,可以使用多媒体定时器获得10微秒以上的精度。
三、 C++中实现定时器的关键
在C++中实现定时器有几个问题要解决:
1、 如何在一个类中管理多个定时器
常见的使用方式中,定时器是一个事件ID和定时间隔的的“键—值”对。CMap是一个dictionary collection class,采用哈希表查找方式来定位,速度十分快,可以尽可能少影响定位精度。但是光保存一个定时间隔键值对还不行,因为不知道当前的定时器计数值(旧preTicks),故还要一个CMap来保存当前ticks。
因此一个定时器对应CMap中的一个“键—值”对。要管理这些定时器,只需要在线程函数中轮询当前定时器的nowTicks,并取得触发间隔elapseTicks,通过比较nowTicks – preTicks > elapseTicks 判断是否到了临界点,掌握定时事件触发时机。
这些工作全由定时器管理类来调度。
2、 使用者(类)应该如何接受定时器事件
可以利用事件或者C++中的虚函数的方式来让使用者(类)接受定时器触发事件,即处理定时器事件的函数被调用。我使用C++抽象类来完成这个工作。
即定义一个定时器事件抽象类,这个抽象类的实现类要成为定时器类的成员变量。当线程判断到定时器触发时,调用这个抽象类的事件处理函数(根据虚拟函数机制的了解,实际调用的实现类的处理函数)。
从上可以看出,如果使用者(类)要用定时器,必须实现定时器抽象类。
3、 谁来启动定时器线程,使用者(类)如何使用定时器管理类来定时。
当然应该由定时器管理类来管理定时线程的启动。使用者要使用定时,必须先实现定时器抽象类,并创建定时器管理类的一个实例,作为其成员变量。
四、 实现
(一)定义定时器抽象类
class ITimerEvent
{
public:
ITimerEvent();
virtual ~ITimerEvent();
void virtual OnTimer(UINT nIDEvent)=0;
};
(二)管理类
class CManTimer
{
public:
CManTimer(ITimerEvent* pEvent);
ITimerEvent* m_pTimerEvent;
virtual ~CManTimer();
void SetTimer(UINT nIDEvent, LARGE_INTEGER nElapse);
void KillTimer(int nIDEvent);
bool Start();
static DWORD WINAPI ThreadProc(LPVOID lpParameter );
HANDLE m_hMutex;
HANDLE m_hThread;
DWORD m_dwThreadId;
BOOL Lock(DWORD dwMilliSec = INFINITE)
{
if( WaitForSingleObject(m_hMutex,dwMilliSec) == WAIT_OBJECT_0)
return TRUE;
return FALSE;
}
BOOL Unlock(DWORD dwMilliSec = INFINITE)
{
return ReleaseMutex(m_hMutex);
}
void Destory();
void TimerProc();
private:
CMap<UINT,UINT,LARGE_INTEGER,LARGE_INTEGER> m_ElapseMap;
CMap<UINT,UINT,LARGE_INTEGER,LARGE_INTEGER> m_TickMap;
}
以下介绍几个关键成员函数的实现:
void CManTimer::SetTimer(UINT nIDEvent, LARGE_INTEGER nElapse)
{
Lock();
m_ElapseMap.SetAt(nIDEvent, nElapse);
LARGE_INTEGER nCurrentTicks = 0;
QueryPerformanceCounter(&nCurrentTicks);
m_TickMap.SetAt(nIDEvent, nCurrentTicks);
Unlock();
}
void CManTimer::KillTimer(int nIDEvent)
{
Lock();
m_ElapseMap.RemoveKey(nIDEvent);
m_TickMap.RemoveKey(nIDEvent);
Unlock();
}
DWORD WINAPI ThreadProc(LPVOID lpParameter )
{
CManTimer* pManTimer = (CManTimer*) lpParameter;
pManTimer->TimerProc();
return 0;
}
void CManTimer::TimerProc()
{
while(1)
{
if(!m_bContinue)
{
ExitThread(0);
}
try
{
POSITION pos = m_ElapseMap.GetStartPosition();
UINT nIDEvent;
LARGE_INTEGER nElapse;
LARGE_INTEGER dTicks;
while (pos != NULL)
{
m_ElapseMap.GetNextAssoc( pos, nIDEvent, nElapse );
m_TickMap.Lookup(nIDEvent,dTicks);
LARGE_INTEGER nCurrentTicks = 0;
QueryPerformanceCounter(&nCurrentTicks);
if(nCurrentTicks - dTicks > nElapse)
{
m_pTimerEvent->OnTimer(nIDEvent);
SetTimer(nIDEvent,nElapse);
}
}
}
catch ( ...) {
TRACE("ManTimer Exception/n");
}
}
}