在精度要求较高的情况下,如要求定时误差不大于1ms时,还可以利用GetTickCount()函数返回自计算机启动后的时间,该函数的返回值是DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。通过两次调用GetTickCount()函数,然后控制它们的差值来取得定时效果.下列的代码可以实现50ms的精确定时,其误差是毫秒级的。
// 起始值和中止值 DWORD dwStart, dwStop ; dwStop = GetTickCount(); while(TRUE) { // 上一次的中止值变成新的起始值 dwStart = dwStop ; // 此处添加相应控制语句 do { dwStop = GetTickCount() ; }while(dwStop - 50 < dwStart) ; } |
用上述两种方式取得的定时效果虽然在许多场合已经满足实际的要求,但由于它们的精度只有毫秒级的,而且在要求定时时间间隔小时,实际定时误差大。对于精确度要求更高的定时操作,则应该使用QueryPerformanceFrequency()和QueryPerformanceCounter()函数。这两个函数是Visual C++提供并且仅供Windows 95及其后续版本使用,其精度与CPU的时钟频率有关,它们要求计算机从硬件上支持精确定时器。QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:
BOOL QueryPerformanceFrequency (LARGE_INTEGER *lpFrequency); BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount); |
上述两个函数的参数的数据类型LARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:
typedef union _LARGE_INTEGER { struct{ DWORD LowPart ; // 4字节整型数 LONG HighPart ; // 4字节整型数 }; LONG QuadPart ; // 8字节整型数 } LARGE_INTEGER ; |
使用QueryPerformanceFrequency()和QueryPerformanceCounter()函数进行精确定时的步骤如下:
1、首先调用QueryPerformanceFrequency()函数取得高精度运行计数器的频率f,单位是每秒多少次(n/s),此数一般很大;
2、在需要定时的代码的两端分别调用QueryPerformanceCounter()以取得高精度运行计数器的数值n1、n2,两次数值的差值通过f换算成时间间隔,t=(n2-n1)/f,当t大于或等于定时时间长度时,启动定时器;
二、编程步骤 1、启动Visual C++6.0,生成一个基于对话框的应用程序,将程序命名为"HightTimer";
2、在对话框面板中添加控件,布局如图一所示,其中包含两个静态文本框,两个编辑框和两个按纽。上面和下面位置的编辑框的ID分别为IDC_TEST和IDC_ACTUAL,"EXIT"按纽的ID为IDOK,"TEST"按纽ID为ID_TEST;
3、通过Class Wizard添加成员变量,两个编辑框控件分别对应为DWORD m_dwTest和DWORD m_dwAct,另外添加"TEST"按纽的鼠标单击消息处理函数;
4、添加代码,编译运行程序。
三、程序代码
///////////////////////////////////////////////////////////////////////// LARGE_INTEGER MySleep(LARGE_INTEGER Interval) // 功能:执行实际的延时功能,Interval 参数为需要执行的延时与时间有关的数量,此函数返回执//行后实际所用的时间有关的数量 ; { LARGE_INTEGER privious, current, Elapse; QueryPerformanceCounter( &privious ); current = privious; while( current.QuadPart - privious.QuadPart < Interval.QuadPart ) QueryPerformanceCounter( ¤t ); Elapse.QuadPart = current.QuadPart - privious.QuadPart; return Elapse; } void CHightTimerDlg::OnTest() { // TODO: Add your control notification handler code here UpdateData(TRUE); //取输入的测试时间值到与编辑框相关联的成员变量m_dwTest中 ; LARGE_INTEGER frequence; //取高精度运行计数器的频率,若硬件不支持则返回FALSE if(!QueryPerformanceFrequency( &frequence)) MessageBox("Your computer hardware doesnt support the high-resolution performance counter", "Not Support", MB_ICONEXCLAMATION | MB_OK); LARGE_INTEGER test, ret; //通过频率换算微秒数到对应的数量(与CPU时钟有关),1秒=1000000微秒; test.QuadPart = frequence.QuadPart * m_dwTest / 1000000; ret = MySleep( test ); //调用此函数开始延时,返回实际花销的数量 ; m_dwAct = (DWORD)(1000000 * ret.QuadPart / frequence.QuadPart ); //换算到微秒数; UpdateData(FALSE); //显示到对话框面板 ; } |
四、小结 本实例介绍了实现精确定时的不同方法,尤其是对于需要精确到微秒级别的定时处理,给出了实现的方法和代码,细心的读者朋友在运行程序的过程中可能会发现要求的定时长度和实际返回的时间长度还是有一些差异的,造成上述情况的原因是由于在进行定时处理时,还需要运行一些简单的循环代码,所以会产生微秒级的误差。