在C++编程中,定时器是一种常见的需求,用于执行周期性或延时任务。Windows操作系统提供了多种定时器实现方式,但这些不同的实现方法可能会给初学者带来困惑。本文将介绍一种使用Windows API实现的简单定时器,并展示如何通过模板类来简化定时器的实现过程。
定时器是一种计时工具,它能够在指定的时间间隔后触发一个事件。在C++中,定时器的实现通常依赖于操作系统提供的API。Windows操作系统提供了多种定时器API,例如CreateTimerQueueTimer
、SetTimer
等。这些API各有特点,适用于不同的场景。
CreateTimerQueueTimer
是Windows API中用于创建定时器的一种方法。它允许开发者指定一个回调函数,在定时器触发时执行。这种方法适用于执行短任务,因为它会在定时器线程中执行回调函数。
需要注意的是,Windows定时器的精度受到操作系统的限制,可能存在一定的误差。经过测试,发现定时器的分辨率在200毫秒以上的间隔时较为准确。
定时器的启动和停止是其核心功能。在本文的实现中,提供了两种方法来启动定时器:Start
方法的默认实现允许定时器在指定的毫秒间隔后启动;第一个重载允许定时器立即启动,即第一次定时事件立即被调用;第二个重载允许定时器只被调用一次。
定时器的停止可以通过Stop
方法实现。如果需要,在OnTimedEvent
函数中也可以调用Stop
方法来停止定时器。例如,可以在定时器被调用10次后停止定时器。
定时器的回调函数是定时器触发时执行的函数。在本文的实现中,使用模板类TTimer
来实现回调函数。这允许开发者指定一个类函数作为回调函数,通过SetTimedEvent
方法设置。
类CTimer
实现了一个虚拟方法OnTimedEvent
,该方法由定时器过程TimerProc
调用。TTimer
类继承自CTimer
,并重写了OnTimerEvent
方法,该方法调用用户定义的回调函数。
为了实现线程安全的定时器事件计数,本文的实现使用了InterlockedExchangeAdd
和InterlockedExchange
函数。这些函数提供了一种快速的互斥访问方式,允许在代码的任何地方读取或设置定时器事件计数。
为了便于使用,本文的定时器实现被封装在单个文件TemplateTimer.h
中。要使用这个定时器,只需将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, 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() {
// 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(param);
timer->SetCount(timer->GetCount() + 1);
timer->OnTimedEvent();
}
//////////////////////////////////////////////////////////////////////////
//
// template class TTimer
//
template
class TTimer : public CTimer {
public:
typedef 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 timer1;
TTimer timer2;
};
void CTimerTest::OnTimedEvent1() {
printf("\nTimer 1 Called (count=%i)", timer1.GetCount());
}
void CTimerTest::OnTimedEvent2() {
printf("\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
getchar(); // Wait for return (stop)
timer1.Stop(); // Stop timer 1
timer2.Stop(); // Stop timer 2
printf("\nTimers stopped (hit return to exit)");
getchar();
}