简单的 C++ 定时器包装器






4.78/5 (14投票s)
TemplateTimer:一个方便且简单的 MSVC / C++ 定时器封装,适用于 Windows,使用模板,可以轻松地为定时事件回调指定类函数。
引言
有各种各样的 Windows 定时器可用,以及它们不同的实现方法,这使得将一个简单的定时器包含到一段源代码中的任务相当令人困惑,特别是对于 C++ 新手而言。TemplateTimer
没有任何特别之处,但为需要在 C++ 类中实现简单定时器的程序员提供了一个方便的封装。
代码讨论
在此实现中,已使用 CreateTimerQueueTimer()
作为定时器,但有几种不同的定时器可用。 CreateTimerQueueTimer()
已使用 WT_EXECUTEINTIMERTHREAD
(对于短任务)实现,但可以更改以满足特定要求。 应该注意的是,此定时器(与所有 Windows 定时器一样)存在固有的非实时操作系统不准确性。 性能取决于系统;但是,经过测试,定时器分辨率在 200 毫秒及以上的间隔内是准确的。
Start()
方法有两个重载。 默认方法只是允许以毫秒为单位的间隔启动定时器。 第一个重载允许立即启动定时器(即,立即调用第一个定时事件)。 第二个重载允许仅调用一次定时器。
模板类 TTimer
使用模板实现回调。 这允许通过 SetTimedEvent()
指定类函数作为回调函数。
CTimer
实现一个由定时器过程 TimerProc()
调用的虚拟方法 OnTimedEvent()
。 TTimer
派生自 CTimer
,并重写 OnTimerEvent()
方法,该方法又调用(用户定义的)回调函数。
GetCount()
和 SetCount()
使用 InterlockedExchangeAdd()
和 InterlockedExchange()
提供一个线程安全的定时器事件计数,可以在代码中的任何位置读取(或设置)。 使用 InterlockedExchange()
提供了一种方便的方式来为数字提供非常快速的互斥访问。
Stop()
方法用于停止定时器。 如果需要,可以在 OnTimedEvent()
函数中使用它,例如在 10 次(10 秒)后停止 timer1
,使用:if( timer1.GetCount() == 10 ){ timer1.Stop(); }
。
Using the Code
为方便起见,TTimer
和 CTimer
类已在单个文件(TemplateTimer.h)中实现。 要使用 TemplateTimer
,只需将 TemplateTimer.h 包含到您的项目中,并按照测试代码 TimerTest.cpp/.h 中所示实现即可。
#pragma once
#include <atlbase.h>
static void CALLBACK TimerProc(void*, BOOLEAN);
///////////////////////////////////////////////////////////////////////////////
//
// class CTimer
//
class CTimer
{
public:
CTimer()
{
m_hTimer = NULL;
m_mutexCount = 0;
}
virtual ~CTimer()
{
Stop();
}
bool Start(unsigned int interval, // interval in ms
bool immediately = false,// true to call first event immediately
bool once = false) // true to call timed event only once
{
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()
{
// Override in derived class
}
void SetCount(int value)
{
InterlockedExchange( &m_mutexCount, value );
}
int GetCount()
{
return InterlockedExchangeAdd( &m_mutexCount, 0 );
}
private:
HANDLE m_hTimer;
long m_mutexCount;
};
///////////////////////////////////////////////////////////////////////////////
//
// TimerProc
//
void CALLBACK TimerProc(void* param, BOOLEAN timerCalled)
{
CTimer* timer = static_cast<CTimer*>(param);
timer->SetCount( timer->GetCount()+1 );
timer->OnTimedEvent();
};
///////////////////////////////////////////////////////////////////////////////
//
// template class TTimer
//
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;
};
这是示例代码(请参阅上面的“下载演示项目”链接)。
#pragma once
#include "TemplateTimer.h"
class CTimerTest
{
public:
void RunTest();
private:
void OnTimedEvent1();
void OnTimedEvent2();
TTimer<CTimerTest> timer1 ;
TTimer<CTimerTest> timer2 ;
};
#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); // Start timer 1 every 1s
timer2.SetTimedEvent(this, &CTimerTest::OnTimedEvent2);
timer2.Start(2000); // Start timer 2 every 2s
// Do something, in this case just wait for user to hit return
getchar(); // Wait for return (stop)
timer1.Stop(); // Stop timer 1
timer2.Stop(); // Stop timer 2
printf("\r\nTimers stopped (hit return to exit)");
getchar();
}