为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

C++延时

2011-12-11 7页 pdf 441KB 36阅读

用户头像

is_002814

暂无简介

举报
C++延时 ::首页 >> 文档中心 >> 在线杂志 >> WIN32 API [ 在线杂志 第 37 期 ] [ 原创文档 本文适合中级读者 已阅读 99007 次 ] 文档 VC 中基于 Windows 的精确定时 中国科学院光电技术研究所 游志宇 示例工程下载 在工业生产控制系统中,有许多需要定时完成的操作,如定时显示当前时间,定时刷新屏 幕上的进度条,上位 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的 ...
C++延时
::首页 >> 文档中心 >> 在线杂志 >> WIN32 API [ 在线杂志 第 37 期 ] [ 原创文档 本文适合中级读者 已阅读 99007 次 ] 文档 VC 中基于 Windows 的精确定时 中国科学院光电技术研究所 游志宇 示例工程下载 在工业生产控制系统中,有许多需要定时完成的操作,如定时显示当前时间,定时刷新屏 幕上的进度条,上位 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的 实时控制系统和数据采集系统中,就更需要精确定时操作。 众所周知,Windows 是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来 完成的。 这样就带来了一些问题,如一旦计算机的 CPU 被某个进程占用,或系统资源紧张时, 发送到消息队列 中的消息就暂时被挂起,得不到实时处理。因此,不能简单地通过 Windows 消息引发一个对定时要求 严格的事件。另外,由于在 Windows 中已经封装了计算机底层硬件 的访问,所以,要想通过直接利用 访问硬件来完成精确定时,也比较困难。所以在实际应用时, 应针对具体定时精度的要求,采取相适 应的定时方法。 VC 中提供了很多关于时间操作的函数,利用它们控制程序能够精确地完成定时和计时操 作。本文详细介绍了 VC 中基于 Windows 的精确定时的七种方式,如下图所示: 图一 图像描述 方式一:VC 中的 WM_TIMER 消息映射能进行简单的时间控制。首先调用函数 SetTimer() 设置定时 间隔,如 SetTimer(0,200,NULL)即为设置 200ms 的时间间隔。然后在应用程序中增 加定时响应函数 OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操 作。这种定时方法非常 简单,可以实现一定的定时功能,但其定时功能如同 Sleep()函数的延 时功能一样,精度非常低,最小 计时精度仅为 30ms,CPU 占用低,且定时器消息在多任务操 作系统中的优先级很低,不能得到及时响 应,往往不能满足实时控制环境下的应用。只可以用 来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的 Timer1。 方式二:VC 中使用 sleep()函数实现延时,它的单位是 ms,如延时 2 秒,用 sleep(2000)。 精度非常 低,最小计时精度仅为 30ms,用 sleep 函数的不利处在于延时期间不能处理其他的 消息,如果时间太 长,就好象死机一样,CPU 占用率非常高,只能用于要求不高的延时程序中。 如示例工程中的 Timer2。 方式三:利用 COleDateTime 类和 COleDateTimeSpan 类结合 WINDOWS 的消息处理过程 来实现秒级延时。如示例工程中的 Timer3 和 Timer3_1。以下是实现 2 秒的延时代码: COleDateTime start_time = COleDateTime::GetCurrentTime(); COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time; while(end_time.GetTotalSeconds()< 2) //实现延时 2 秒 { MSG msg; GetMessage(&msg,NULL,0,0); TranslateMessage(&msg); DispatchMessage(&msg); //以上四行是实现在延时或定时期间能处理其他的消息, //虽然这样可以降低 CPU 的占有率, //但降低了延时或定时精度,实际应用中可以去掉。 end_time = COleDateTime::GetCurrentTime()-start_time; }//这样在延时的时候我们也能够处理其他的消息。 方式四:在精度要求较高的情况下,VC 中可以利用 GetTickCount()函数,该函数的返回值 是 DWORD 型,表示以 ms 为单位的计算机启动后经历的时间间隔。精度比 WM_TIMER 消息 映射高,在较 短的定时中其计时误差为 15ms,在较长的定时中其计时误差较低,如果定时时 间太长,就好象死机一样,CPU 占用率非常高,只能用于要求不高的延时程序中。如示例工程 中的 Timer4 和 Timer4_1。下列代码可以实现 50ms 的精确定时: DWORD dwStart = GetTickCount(); DWORD dwEnd = dwStart; do { dwEnd = GetTickCount()-dwStart; }while(dwEnd <50); 为使 GetTickCount()函数在延时或定时期间能处理其他的消息,可以把代码改为: DWORD dwStart = GetTickCount(); DWORD dwEnd = dwStart; do { MSG msg; GetMessage(&msg,NULL,0,0); TranslateMessage(&msg); DispatchMessage(&msg); dwEnd = GetTickCount()-dwStart; }while(dwEnd <50); 虽然这样可以降低 CPU 的占有率,并在延时或定时期间也能处理其他的消息,但降低了延时或 定时精度。 方式五:与 GetTickCount()函数类似的多媒体定时器函数 DWORD timeGetTime(void),该 函数定时精 度为 ms 级,返回从 Windows 启动开始经过的毫秒数。微软公司在其多媒体 Windows 中提供了精确定时器的底 层 API 持,利用多媒体定时器可以很精确地读出系统的当 前时间,并且能在非常精确的时间间隔内完成一 个事件、函数或过程的调用。不同之处在于调 用 DWORD timeGetTime(void) 函数之前必须将 Winmm.lib 和 Mmsystem.h 添加到工程中, 否则在编译时提示 DWORD timeGetTime(void)函数未定义。由于使用该 函数是通过查询的方 式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。如示例工程中的 Timer5 和 Timer5_1。 方式六:使用多媒体定时器 timeSetEvent()函数,该函数定时精度为 ms 级。利用该函数 可以实现周期性的函数调用。如示例工程中的 Timer6 和 Timer6_1。函数的原型如下: MMRESULT timeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, WORD dwUser, UINT fuEvent ) 该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被 激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回 NULL。函数的参数 说明如下: uDelay:以毫秒指定事件的周期。 Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。 缺省值为 1ms。 LpTimeProc:指向一个回调函数。 DwUser:存放用户提供的回调数据。 FuEvent:指定定时器事件类型: TIME_ONESHOT:uDelay 毫秒后只产生一次事件 TIME_PERIODIC :每隔 uDelay 毫秒周期性地产生事件。 具体应用时,可以通过调用 timeSetEvent()函数,将需要周期性执行的任务定义在 LpTimeProc 回调函数 中 (如:定时采样、控制等 ),从而完成所需处理的事件。需要注意的是, 任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用 timeKillEvent()将之释放。 方式七:对于精确度要求更高的定时操作,则应该使用 QueryPerformanceFrequency()和 QueryPerformanceCounter()函数。这两个函数是 VC 提供的仅供 Windows 95 及其后续版本使 用的精确时间函数,并要求计算机从硬件上支持精确定时器。如示例工程中的 Timer7、 Timer7_1、Timer7_2、Timer7_3。 QueryPerformanceFrequency()函数和 QueryPerformanceCounter()函数的原型如下: BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount); 数据类型 ARGE_INTEGER 既可以是一个 8 字节长的整型数,也可以是两个 4 字节长的整型 数的联合结构, 其具体用法根据编译器是否支持 64 位而定。该类型的定义如下: typedef union _LARGE_INTEGER { struct { DWORD LowPart ;// 4 字节整型数 LONG HighPart;// 4 字节整型数 }; LONGLONG QuadPart ;// 8 字节整型数 }LARGE_INTEGER ; 在进行定时之前,先调用 QueryPerformanceFrequency()函数获得机器内部定时器的时钟 频率, 然后在需要严格定时的事件发生之前和发生之后分别调用 QueryPerformanceCounter() 函数,利用两次获得的计数之差及时钟频率,计算出事件经 历的精确时间。下列代码实现 1ms 的精确定时: LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率 QueryPerformanceCounter(&litmp); QPart1 = litmp.QuadPart;// 获得初始值 do { QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//获得中止值 dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒 }while(dfTim<0.001); 其定时误差不超过 1 微秒,精度与 CPU 等机器配置有关。 下面的程序用来测试函数 Sleep(100)的精确持续时间: LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率 QueryPerformanceCounter(&litmp); QPart1 = litmp.QuadPart;// 获得初始值 Sleep(100); QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//获得中止值 dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒 由于 Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。下列代码实现 1 微秒的精确定时: LARGE_INTEGER litmp; LONGLONG QPart1,QPart2; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率 QueryPerformanceCounter(&litmp); QPart1 = litmp.QuadPart;// 获得初始值 do { QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//获得中止值 dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒 }while(dfTim<0.000001); 其定时误差一般不超过 0.5 微秒,精度与 CPU 等机器配置有关。 (完 ) 最新评论 [发表评论 ] [文章投稿 ] 查看所有评论 推荐给好友 打印 很全面!学习了。 ( guhqjack 发表于 2010-8-7 7:22:00) 我稍微修改后测了一下, void CMulti_TimerDlg::OnButtonTime71() { for(int i=0;i<1000000;i++) { Delay1us(); } MessageBox("1 妙延时已到 !\n"); } void CMulti_TimerDlg::Delay1us() { LARGE_INTEGER l itmp; LONGLONG QPart1,QPart2;; double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp); dfFreq = (double)li tmp.QuadPart; / / 获得计数器的时钟频率 QueryPerformanceCounter(&litmp); QPart1 = li tmp.QuadPart; // 获得初始值 do{ QueryPerformanceCounter(&litmp); QPart2 = li tmp.QuadPart;/ / 获得中止值 dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq; / / 获得对应的时间值,单位为秒 }while(dfTim<0.000001); } 本来应该是在点击后 1s 钟弹出对话框的,但是时间上却花了 2.47s,不知道是我的理解有误,还是怎么回事?哪位 达人给解释下?谢谢! ( spurs 发表于 2009-1-19 16:21:00) 中国科学院光电技术研究所, 大连理工大学一位博士 都好有名头啊。 ( wei8010 发表于 2007-11-26 18:21:00) 晕 . .. .. .. .. . 哎 . .. .. .. .. . ( wei8010 发表于 2007-11-26 18:17:00) 记得没错的话这篇文章应该是大连理工大学一位博士的原创。 ( four 发表于 2007-3-30 20:39:00) 找到我问题的原因了,我时间处理时间要大于周期 1ms 。好东西,太精确了。 ( lzl1010 发表于 2006-3-17 16:26:00) 我用的是第 7 种定时器。 ( lzl1010 发表于 2006-3-17 16:21:00) 好。我有一个程序,每 1ms 读一次 usb 设备,按开始按钮时开始读,按停止按钮停止读。按停止按钮停不下来, 之后程序就没有回应了,查看 cpu 使用率为 100%,太占系统资源了。 ( lzl1010 发表于 2006-3-17 16:18:00) 你好 ,我是数据库编程的初学者 ,请问当点击一个按钮进入下一页面的时候有时总是会出现一个错误 :提示指针 错误或者检查记录出错 ,但还是能进入下一页面 ,请问是为什么 ? 还有就是无效的游标状态又是怎么回事 ?? ( 153375424 发表于 2005-11-28 0:18:00) 你好 ,我是数据库编程的初学者 ,请问当点击一个按钮进入下一页面的时候有时总是会出现一个错误 :提示指针 错误或者检查记录出错 ,但还是能进入下一页面 ,请问是为什么 ? 还有就是无效的游标状态又是怎么回事 ?? ( 153375424 发表于 2005-11-28 0:18:00) . .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. More. .. 版权所有 © 1999 - 2010 VC 知识库
/
本文档为【C++延时】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索