Windows实现高精度定时器的三种方法
程序员文章站
2022-06-09 16:33:47
...
前段时间一个项目需要用到1/24s的高精度定时器,每秒的误差不能超过10ms,大约41.666666666毫秒的延时,普通Sleep肯定是没办法满足的了,可以用以下新的三种方法:
/*
// 1秒=1000毫秒(ms)
// 1毫秒=1/1000秒(s)
// 1秒=1000000 微秒(μs)
// 1微秒=1/1000000秒(s)
// 1秒=1000000000 纳秒(ns)
// 1纳秒=1/1000000000秒(s)
// lTime----休眠时间(微秒)
*/
1、利用CreateWaitableTimer实现纳秒级延时
/*
纳秒休眠,符号ns(英语:nanosecond ).
1纳秒等于十亿分之一秒(10-9秒)
1 纳秒 = 1000皮秒
1,000 纳秒 = 1微秒
1,000,000 纳秒 = 1毫秒
1,000,000,000 纳秒 = 1秒
*/
int NSSleep()
{
HANDLE hTimer = NULL;
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -390000;
// Create a waitable timer.
hTimer = CreateWaitableTimer(NULL, TRUE, "WaitableTimer");
if (!hTimer)
{
printf("CreateWaitableTimer failed (%d)\n", GetLastError());
return 1;
}
// Set a timer to wait for 10 seconds.
if (!SetWaitableTimer(
hTimer, &liDueTime, 0, NULL, NULL, 0))
{
printf("SetWaitableTimer failed (%d)\n", GetLastError());
return 2;
}
// Wait for the timer.
if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)
printf("WaitForSingleObject failed (%d)\n", GetLastError());
return 0;
}
2、利用QueryPerformanceFrequency与QueryPerformanceCounter实现毫秒级延时
// 休眠指定毫秒数
void MSleep(long lTime)
{
LARGE_INTEGER litmp;
LONGLONG QPart1,QPart2;
double dfMinus, dfFreq, dfTim, dfSpec;
QueryPerformanceFrequency(&litmp);
dfFreq = (double)litmp.QuadPart;
QueryPerformanceCounter(&litmp);
QPart1 = litmp.QuadPart;
dfSpec = 0.000001*lTime;
do
{
QueryPerformanceCounter(&litmp);
QPart2 = litmp.QuadPart;
dfMinus = (double)(QPart2-QPart1);
dfTim = dfMinus / dfFreq;
}while(dfTim<dfSpec);
}
3、利用timeSetEvent实现1ms定时器
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
//定义1ms和2s时钟间隔,以ms为单位
#define ONE_MILLI_SECOND 1
//定义时钟分辨率,以ms为单位
#define TIMER_ACCURACY 1
volatile DWORD g_nCounter = 0;
volatile DWORD g_nCnt = 0;
DWORD g_nTicket = 0;
LARGE_INTEGER g_xliPerfFreq = {0};
LARGE_INTEGER g_xliPerfStart={0};
LARGE_INTEGER g_xliPerfNow={0};
int g_nSecond = 0;
MMRESULT g_mmTimerId = 0;
UINT g_wAccuracy = 0;
// 自己去实现一个PING函数, 网上大把就不发了
int StartPing()
{
return 1;
}
void CALLBACK TimerProc(UINT nID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD d2)
{
double dtime = 0.0f;
char szBuffer[MAX_PATH] = {0x00};
DWORD nTemp = 0;
if(nID == g_mmTimerId)
{
g_nCounter++;
g_nSecond += StartPing();
if(g_nCounter == 24)
{
QueryPerformanceCounter(&g_xliPerfNow);
dtime = ((double)(g_xliPerfNow.QuadPart - g_xliPerfStart.QuadPart) * 1000000.0f) / (double)g_xliPerfFreq.QuadPart;
if(dtime < 1000000.0f)
{
MSleep((1000000.0f - dtime));
}
QueryPerformanceCounter(&g_xliPerfNow);
dtime = ((double)(g_xliPerfNow.QuadPart - g_xliPerfStart.QuadPart) * 1000000.0f) / (double)g_xliPerfFreq.QuadPart;
nTemp = GetTickCount() - g_nTicket;
sprintf(szBuffer," [%04d] 执行时间 %d 毫秒, \t%.9f 微秒, \t延时: %d ms", g_nCnt, nTemp, dtime, g_nSecond);
cout<<szBuffer<<endl;
g_nTicket = GetTickCount();
memset(&g_xliPerfNow, 0x00, sizeof(LARGE_INTEGER));
memset(&g_xliPerfStart, 0x00, sizeof(LARGE_INTEGER));
memset(&g_xliPerfNow, 0x00, sizeof(LARGE_INTEGER));
QueryPerformanceFrequency(&g_xliPerfFreq);
QueryPerformanceCounter(&g_xliPerfStart);
g_nCnt++;
g_nCounter = 0;
g_nSecond = 0;
}
}
}
// 释放定时器
void FreeHighTimer()
{
if(g_mmTimerId == 0)
return;
timeKillEvent(g_mmTimerId);
timeEndPeriod(g_wAccuracy);
}
// 初始化高精度定时器
BOOL InitHighTimer()
{
TIMECAPS tc;
QueryPerformanceFrequency(&g_xliPerfFreq);
QueryPerformanceCounter(&g_xliPerfStart);
g_nTicket = GetTickCount();
//利用函数timeGetDeVCaps取出系统分辨率的取值范围,如果无错则继续;
if(timeGetDevCaps(&tc,sizeof(TIMECAPS)) == TIMERR_NOERROR)
{
//分辨率的值不能超出系统的取值范围
g_wAccuracy = min(max(tc.wPeriodMin, TIMER_ACCURACY), tc.wPeriodMax);
//调用timeBeginPeriod函数设置定时器的分辨率
timeBeginPeriod(g_wAccuracy);
// 设定41毫秒定时器
g_mmTimerId = timeSetEvent(41,0,TimerProc,NULL,TIME_PERIODIC);
if(g_mmTimerId == 0)
{
cout << "timeSetEvent failed: %d" << GetLastError() << endl;
return FALSE;
}
return TRUE;
}
return FALSE;
}
每一秒耗时都是相对准确的,运行效果如下:
本来是创建一个线程,再用方法1、2来休眠实现延时,但这种太低效率了而且不能保证误差,所以综上对比,强烈推荐第三种方法做高精度定时器!