Windows实现高精度定时器的三种方法

      前段时间一个项目需要用到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;
}

每一秒耗时都是相对准确的,运行效果如下:

前面为GetTicket取出的毫秒,后面为每执行24次一个周期所消耗的微妙,最后一个为ping

      本来是创建一个线程,再用方法1、2来休眠实现延时,但这种太低效率了而且不能保证误差,所以综上对比,强烈推荐第三种方法做高精度定时器!

### 回答1: 高精度定时器是指能够实现毫秒级别或更高精度定时器。在MFC实现高精度定时器可以通过以下步骤实现: 1.创建一个窗口,并通过SetTimer函数设置一个定时器。该函数有两个参数,一个是定时器ID号,一个是定时器时间间隔。 2.在窗口类中添加一个成员变量,用于保存已经过去的时间。我们可以在WM_TIMER消息处理函数中,每次处理时递增该变量。 3.通过该变量计算出所需的时间,然后进行相应的操作。例如,我们可以将该变量转化为分钟和秒钟,然后在窗口上显示出来。 4.为了提高定时器的精度,可以通过Win32 API函数timeGetTime获取系统时间,然后在WM_TIMER消息处理函数中计算与上一次时间间隔,从而更加精确地计算已经过去的时间。 需要注意的是,高精度定时器会占用系统资源,并且可能存在时间误差。因此,在实现时需要考虑这些因素,并根据实际需求进行调整。 ### 回答2: 高精度定时器是一种能够实现较为精确的时间计量和延时控制的技术,而MFC(Microsoft Foundation Classes)则是基于Windows操作系统的C++类库,提供了GUI界面开发所需要的各种类、函数和控件等工具。将两者结合使用,可以实现MFC编写的应用程序对时间的更加准确的控制或监测,如毫秒或微秒级别的时间计算和处理等。 要在MFC实现高精度定时器功能,可以考虑使用Win32 API中提供的计时器函数来进行实现。具体实现步骤如下: 1. 定义计时器变量和时间变量。例如: UINT_PTR m_TimerID; // 计时器ID DWORD m_dwStartTime; // 记录开始时间 DWORD m_dwCurrentTime; // 记录当前时间 DWORD m_dwElapsedTime; // 记录已过时间 2. 创建计时器并开始计时。可以在窗口初始化函数中添加如下代码: m_TimerID = SetTimer(1, 1, NULL); // 1ms间隔 m_dwStartTime = GetTickCount(); // 记录开始时间 3. 处理计时器消息。在窗口消息响应函数中,添加对WM_TIMER消息的处理,如: case WM_TIMER: { m_dwCurrentTime = GetTickCount(); // 记录当前时间 m_dwElapsedTime = m_dwCurrentTime - m_dwStartTime; // 计算已过时间 // 这里可以根据需要进行时间数据的显示、处理等其他操作 } break; 4. 在窗口关闭时停止计时器。可以在窗口关闭函数中添加如下代码: KillTimer(m_TimerID); 以上就是使用高精度定时器实现MFC的简单示例。需要注意的是,由于不同计算机的性能和Windows操作系统的版本等因素可能会影响计时器的精度和稳定性,因此在实际应用中需要针对具体需求进行测试和调整。 ### 回答3: 高精度定时器可以通过MFC的计时器来实现MFC的计时器是基于Windows API的定时器实现的。Windows API提供了一个SetTimer函数,用于设置定时器MFC的CWnd类继承了Windows API的CWnd类,在此基础上提供了一系列的计时器函数。 使用MFC计时器,首先需要在类声明中添加一个计时器ID,具体实现可以如下: #define TIMER_ID 1 class CMyDlg : public CDialog { public: CMyDlg(CWnd* pParent = NULL); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_MYDLG_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); protected: HICON m_hIcon; int m_nCount; afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnBnClickedButtonStart(); afx_msg void OnBnClickedButtonStop(); DECLARE_MESSAGE_MAP() }; 在类声明中添加了一个计时器ID为1。同时,在消息映射中,添加了一个响应定时器事件的函数OnTimer。 void CMyDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == TIMER_ID) { m_nCount++; //每次增加计数 } CDialog::OnTimer(nIDEvent); } OnTimer函数响应计时器事件,其中nIDEvent就是计时器ID。在函数中,我们可以编写计时器事件响应的代码,这里是每次增加计数。 在对话框初始化时就设置计时器: BOOL CMyDlg::OnInitDialog() { CDialog::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 SetTimer(TIMER_ID, 500, NULL); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } 在OnInitDialog函数中添加代码SetTimer(TIMER_ID, 500, NULL);就可以设置一个500ms的计时器了。 当然,在对话框关闭时,还要记得取消计时器: void CMyDlg::OnBnClickedButtonStop() { // TODO: 在此添加控件通知处理程序代码 KillTimer(TIMER_ID); } 这样,一个MFC高精度定时器实现了。其实,在MFC中,还可以使用CWnd::SetTimer来设置定时器,不过与SetTimer函数类似,使用时也需要取消,不然会造成内存泄漏。同时,MFC还提供了更为灵活的计时器功能,可以用来处理其他复杂的问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪宁宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值