背景:虽然Win95下可视化开发工具如VC、Delphi、C++ Builder等都有专用的定时器控件Timer,而且使用很方便,可以实现一定的定时功能,但最小计时精度仅为55ms,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响应,往往不能满足实时控制环境下的应用。不过Microsoft公司在Win32 API函数库中已经为用户提供了一组用于高精度计时的底层函数,如果用户使用得当,计时精度可到1ms。这个计时精度、对于一般的实时系统控制完全可以满足要求。
在最近的工作中用到计时器的地方比较多,个人尝试了很多中定时器的实现,用于OnTimer定时不能满足要求,于是根据网上的资料封装了两类计时器,供大家参考。因为自己也是初学者,有问题的话请大家给出指导,下面是我封装的两种计时器的类。
多媒体定时器类:
/*
*作者: FreeBird
*描述:多媒体定时器
*完成时间:2013/7/14
*使用方法:重载函数Run
* 1.先调用SetTimerResolution设置分辨率
* 2.调用SetEventType设置事件类型
* 3.调用GetTimerDelay时间周期
* 以上1到3不设置默认:m_nType = TIME_PERIODIC;m_nDelay = 3000;m_nResolution = 1;
* 4.创建定时器CreateTimer
* 5.用完后销毁定时器DestroyTimer
*/
#pragma once
#include <MMSystem.h>
#pragma comment(lib, "Winmm.lib")
class MultimediaTimer
{
public:
MultimediaTimer(void);
virtual ~MultimediaTimer(void);
public:
//创建多媒体定时器
int CreateTimer();
//销毁对媒体定时器
int DestroyTimer();
//设置分辨率,最小精度1ms,如果设置的超出机器支持的范围,则按照min(机器支持的最小值,1)来设置
int SetTimerResolution(int nResolution);
//获取调用者自己设定的分辨率
int GetTimerResolution() const;
//设置以毫秒指定事件的周期
int SetTimerDelay(int nDelay);
//获取定时器时间周期
int GetTimerDelay() const;
//设置定时器事件类型
void SetEventType(int nEventType);
//获取定时器事件类型
int GetEventType() const;
//多媒体定时器回调函数
static void CALLBACK TimerCallBackPro(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2);
public:
//子类重载该函数实现不同定时功能
virtual void Run();
private:
//获得系统支持的计时器装置驱动所支援的最小的解析度值(延时的精度),返回设置的分辨率,返回负数表示失败
int GetSystemPeriod(int &nPeriodMix, int &nPeriodMax);
private:
//计时器的精度,默认设置机器支持的最小精度,一般是1ms
MMRESULT m_nTimerID;
//分辨率
int m_nResolution;
//以毫秒指定事件的周期
int m_nDelay;
//设置定时器事件类型
int m_nType;
//定时器事件类型
enum eEventType
{
RunOnce = TIME_ONESHOT, //uDelay毫秒后只产生一次事件
RunCycle = TIME_PERIODIC //每隔uDelay毫秒周期性地产生事件。
};
};
#include "StdAfx.h"
#include "MultimediaTimer.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
MultimediaTimer::MultimediaTimer(void)
{
m_nTimerID = 0;
m_nType = TIME_PERIODIC;
m_nDelay = 1000;
m_nResolution = 1;
}
MultimediaTimer::~MultimediaTimer(void)
{
}
int MultimediaTimer::CreateTimer()
{
if ( timeBeginPeriod(m_nResolution) != TIMERR_NOERROR )
return -1;
m_nTimerID = timeSetEvent(m_nDelay, m_nResolution, (LPTIMECALLBACK)TimerCallBackPro, (DWORD)this, m_nType);
if ( m_nTimerID == 0)
{
timeEndPeriod(m_nResolution);
return -2;
}
return 0;
}
int MultimediaTimer::DestroyTimer()
{
if ( m_nTimerID )
{
timeKillEvent(m_nTimerID);
timeEndPeriod(m_nResolution);
m_nTimerID = 0;
}
return 0;
}
int MultimediaTimer::SetTimerResolution( int nResolution )
{
int nPeriodMin; //系统支持的最小值
int nPeriodMax; //系统支持的最大值
//获得机器支持的分辨路范围
if ( GetSystemPeriod(nPeriodMin, nPeriodMax) != 0 )
return -1;
//如果超出机器支持范围按照min(机器支持的最小值,1)来设置
if ( nResolution < nPeriodMin || nResolution > nPeriodMax )
{
m_nResolution = min( max(nPeriodMin, 1), nPeriodMax );
}
else
{
m_nResolution = nResolution;
}
return 0;
}
int MultimediaTimer::GetTimerResolution() const
{
return m_nResolution;
}
int MultimediaTimer::GetSystemPeriod(int &nPeriodMix, int &nPeriodMax)
{
TIMECAPS tc;
//向机器申请一个多媒体定时器
if ( timeGetDevCaps(&tc,sizeof(TIMECAPS)) != TIMERR_NOERROR )
return -1;
nPeriodMix = tc.wPeriodMin;
nPeriodMax = tc.wPeriodMax;
return 0;
}
int MultimediaTimer::SetTimerDelay(int nDelay)
{
m_nDelay = nDelay;
return 0;
}
int MultimediaTimer::GetTimerDelay() const
{
return m_nDelay;
}
void MultimediaTimer::Run()
{
}
void MultimediaTimer::TimerCallBackPro(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2)
{
MultimediaTimer *pMulTime = (MultimediaTimer *)dwUser;
pMulTime->Run();
}
void MultimediaTimer::SetEventType(int nEventType)
{
m_nType = nEventType;
}
int MultimediaTimer::GetEventType() const
{
return m_nType;
}
用线程实现的时间核心对象类:
#include <winbase.h> //包含了Windows平台专属的一些函数和宏的定义
#include <windows.h>
#include <process.h> //一般用程序获得进程列表时会用这个文件中的API
class TimerThread
{
protected:
HANDLE _TerminateEvent; //事件句柄
HANDLE _TimerHandle; //时间内核对象
HANDLE _hHandle; //线程句柄
unsigned _ThreadID; //线程ID
HWND _hNotifyWnd; // 通知窗口
LONG _TimerHandleCount; //时间内核对象调用次数
LONG _lPeriodTime; //定时器周期
SYSTEMTIME _st; //指定开始执行时间
int _lSecond; //指定多少秒后开始执行
CRITICAL_SECTION _Mutex; //临界区
protected:
virtual void OnInit(); //初始化,子类需要重载的函数
virtual void OnTimer()=0; //定时器函数,子类必须重载的函数
virtual void OnExit(); //退出,子类需要重载的函数
static UINT WINAPI StaticThreadProc(LPVOID lpPara);
virtual void Run(); //运行
public:
TimerThread(int nPriority = THREAD_PRIORITY_NORMAL); //构造函数中创建事件对象、时间内核对象、初始化临界区并启动线程
virtual ~TimerThread();
BOOL SetTimer(LONG lPeriodTime); //设置定时器,周期执行ms,该函数调用后立即按周期执行
BOOL SetTimer(SYSTEMTIME st, LONG lPeriodTime); //调用该函数后,在st指定时间到时,按照周期执行
BOOL SetTimer(int lSecond, LONG lPeriodTime); //调用该函数后,在指定lSecond秒后开始执行
void Terminate(); //终止定时器
HANDLE GetHandle(); //获取线程句柄
inline void SetWnd(HWND hWnd); //关联消息的窗口句柄
LONG GetFinishedCount(); //获取线程被执行次数
};
void TimerThread::SetWnd(HWND hWnd) //关联消息的窗口句柄
{
// assert(::IsWindow(hWnd));
_hNotifyWnd = hWnd;
}
#endif _TIMER_THREAD_H_
/*
作者:FreeBird
功能:时间核心对象线程类,该类是抽象类,需要子类实现OnTimer函数,实现定时器的功能
*/
#ifndef _TIMER_THREAD_H_
#define _TIMER_THREAD_H_
#include <objbase.h> //主持COM的一个头文件
#include "TimerThread.h"
TimerThread::TimerThread(int nPriority)
{
_TimerHandleCount = 0;
_TerminateEvent = CreateEvent(0, TRUE, FALSE, NULL); //创建线程
_TimerHandle = CreateWaitableTimer(NULL, FALSE, NULL); //创建时间核心对象,自动重置定时器
::InitializeCriticalSection(&_Mutex); //初始化临界区
unsigned int id;
_hHandle = (HANDLE)_beginthreadex(NULL, 0, StaticThreadProc, this, 0, &id);
_ThreadID = id;
if(_hHandle != NULL)
SetThreadPriority(_hHandle, nPriority); //设置线程优先级
//else
// MessageBox(NULL, "Create thread fail!", "TimerThread", MB_OK);
}
TimerThread::~TimerThread()
{
Terminate();
CloseHandle(_hHandle);
CloseHandle(_TimerHandle);
CloseHandle(_TerminateEvent);
::DeleteCriticalSection(&_Mutex);
}
//子类需要重载的函数
void TimerThread::OnInit()
{
}
//OnTimer()函数需要在子类实现
//子类需要重载的函数
void TimerThread::OnExit()
{
}
BOOL TimerThread::SetTimer(LONG lPeriodTime)//ms
{
_lPeriodTime = lPeriodTime;
LARGE_INTEGER liUTC;
memset(&liUTC, 0, sizeof(LARGE_INTEGER));
return SetWaitableTimer(_TimerHandle, &liUTC, _lPeriodTime, NULL, NULL, FALSE)!=0;
}
BOOL TimerThread::SetTimer(SYSTEMTIME st, LONG lPeriodTime)//ms
{
_lPeriodTime = lPeriodTime;
_st = st; //SYSTEMTIME结构,用来设置第1次通知的时间
FILETIME ftLocal, ftUTC; //FILETIME结构,用来接受STSTEMTIME结构的转换
LARGE_INTEGER liUTC; //LARGE_INTEGER结构,作为SetWaitableTimer的参数,定时时间
SystemTimeToFileTime(&_st, &ftLocal); //将SYSTIME结构转换为FILETIME结构
LocalFileTimeToFileTime(&ftLocal, &ftUTC); //将本地时间转换为标准时间(UTC),SetWaitableTimer函数接受一个标准时间
liUTC.LowPart = ftUTC.dwLowDateTime; // 设置LARGE_INTEGER结构,因为该结构数据要作为SetWaitableTimer的参数
liUTC.HighPart = ftUTC.dwHighDateTime;
return SetWaitableTimer(_TimerHandle, &liUTC, _lPeriodTime, NULL, NULL, FALSE) != 0;
}
BOOL TimerThread::SetTimer(int lSecond, LONG lPeriodTime)
{
_lSecond = lSecond;
_lPeriodTime = lPeriodTime;
LARGE_INTEGER li;
const int nTimerUnitsPerSecond = 1000000000 / 100; //每1s中有多少个100ns
li.QuadPart = -(_lSecond * nTimerUnitsPerSecond ); //负数,表示相对值lSecond秒
return SetWaitableTimer(_TimerHandle, &li, _lPeriodTime, NULL, NULL, FALSE)!=0;
}
void TimerThread::Terminate()
{
SetEvent(_TerminateEvent);
if(WaitForSingleObject(_hHandle, 200) != WAIT_OBJECT_0)
TerminateThread(_hHandle, 0);
}
void TimerThread::Run()
{
HANDLE HandleArray[2];
HandleArray[0] = _TimerHandle;
HandleArray[1] = _TerminateEvent;
for(;;)
{
DWORD ret = WaitForMultipleObjects(2, HandleArray, false, INFINITE); //当其中有一个信号量是往下执行
if(ret == WAIT_OBJECT_0 + 1) //第二个事件发生
break;
if(ret == WAIT_OBJECT_0) //第一个事件发生
{
try
{
OnTimer();
}
catch (...)
{
}
InterlockedIncrement(&_TimerHandleCount); //共享变量,互斥锁,时间内核对象被调用次数
}
}
}
HANDLE TimerThread::GetHandle()
{
return _hHandle;
}
LONG TimerThread::GetFinishedCount()
{
return _TimerHandleCount;
}
UINT WINAPI TimerThread::StaticThreadProc(LPVOID lpPara)
{
TimerThread *pObj = (TimerThread*)lpPara;
pObj->OnInit();
pObj->Run();
pObj->OnExit();
return 0;
}