除了Sleep(),有没有比OnTimer()更精确的计时器?

neogeo60b 2001-08-01 11:33:49
如果有,应该怎么用?
...全文
416 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
young_t 2001-08-01
  • 打赏
  • 举报
回复
1.前言。
我们知道,在Windows中,可以通过调用SetTimer函数为应用程序分配一个计时器。
当指定了一个时间间隔以后,Windows系统将每隔指定的时间向应用发送一条WM_TIMER
消息,从而使应用程序能够实现许多与时间相关的动作。
然而需要指出的是,由系统发给应用程序的WM_TIMER消息并不是异步的,这条消息
被放在常规的消息队列中,并与其它消息一起排序,因此,即使我们在调用SetTimer()时
设定了1000毫秒的时间间隔,应用程序却不一定保证每隔一秒钟接受到一条WM_TIMER消
息。如果另一个程序的忙碌时间超过一秒钟,那么我们的应用程序在那段时间内就不能接
受到任何WM_TIMER消息。
显然,这种情况的存在对那些需要精确时间间隔的应用(如某些监控程序)来说是致
命的。所幸的是,在Windows中隐藏着某些机制,使得我们能够获得精确计时的服务。

2.系统计时器.

在Windows的SYSTEM.DRV驱动程序中提供了几个鲜为人知的系统计时器函数(这几个函数未
写入Windows.h中,但却被SYSTEM.DRV输出了),这几个函数可以帮助我们获得精确计时器
服务,即系统计时器。这其中最重要的是CreateSystemTimer()和KillSystemTimer(),这
两个函数允许我们安异步计时器的回调函数(Callback),有些类似于在DOS环境中截取
INT 8中断处理程序。这个回调是真正异步的,完全避开了Windows的消息工具,因而具有
重要意义。事实上,Microsoft Excel和Windows COMM驱动程序都用到了系统计时器,而由
SetTimer()安装的一般计时器也是由系统计时器来实现的。

这两个函数的原型如下:
WORD CreateSystemTimer(wMsecInterval,lpfnTimerProc);
WORD wMsecInterval; /*以毫秒为单位的时间间隔,系统将每隔此时间调用一 次回调函数*
/
FARPROC lpfn TimerProc;/*指向回调函数的指针*/
WORD KillSystemTimer(hTimer);
WORD hTimer;/*欲释放的系统计时器句柄*/

其中,CreateSystemTimer()用于安装一个系统计时器回调函数,SYSTEM
INT8处理程序将按wMsecInterval指定的时时间间隔调用此回调函数。当然,这个指定的回?
髌德室 是有限的,同SetTimer()一样,每秒钟调用回调函数次数不能超过18.2次,即wMsec
Interval>55。该函数返回一个系统计时器句柄。若安装失败,则返回NULL。KillSystemTim
er()则用于撤销
一个已安装的系统计时器hTimer。若成功,则返回;出错则返回传给它的参数hTimer。

3.使用系统计时器应注意的问题。

系统计时器回调函数虽然不是中断处理程序,但由于它直接被中断处理程序调用,因此也必
将它看作中断代码。这也就决定了在使用过程中必须注意以下几个问题:
(1).在回函数中应包括尽量少的代码,以使得频繁回调的该函数不至于占用太多的CPU时间?
一般情况下,系统计时器总是用来监视或设置某些变量的值。
(2).由于该回调函数属于中断代码,因此大多数Windows API函数调用都不适用了,只有几?
简单的函数仍然可以使用,如PostMessage(),GetCurrentTask()和MessageBeep()等。
(3).由于该回调函数由中断处理程序直接调用,因此该函数必须放在一个固定的代码段中,
并且调用前必须装载DS寄存器,这可由形实替换函数MakeProcInstance()来做到。另外,
由于这两个函数在Windows.h中没有给出(即Windows缺省输入库不含此两函数),因此在调用
之前必须进行链
接。这可采用运行时动态链接,即通过GetModuleHandle()和GetProcAddress()来链接;也?
在程序模块定义文件中用IMPORTS语句来引入,此时则必须在程序源文件中说明CreateSyst
emTimer()和KillSystemTimer()为外部函数。本文给出的例子采用第二种方案。

4.一个例子。

本文最后给出一个简单的例子,以说明系统计时器是如何工作的。在本例中,我们安装了一
个每秒钟调用一次的回调函数,该回调函数发出一声蜂鸣。为了测试该系统计时器,我们特
意编写了一段较长时间的循环语句。在这段循环中,由SetTimer()安装的通常计时器是不能
工作的(因为Win
dows是一个非抢先的系统),而我们安装的系统计时器仍然能每隔一秒钟发出一声蜂鸣。?
美 子在MSVC++1.5中调试通过,运行良好。
//SystemT.c
#include<windows.h>
extern WORD WINAPI CreateSystemTimer(WORD wTimeOut,FARPROC lpfnTimerProc);
extern WORD WINAPI KillSystemTimer(WORD hTimer);
void FAR PASCAL_export MyTimerProc(void);
WORD SetUpSystemTimer(WORD wTimeOut);
BOOL ClearSystem Timer(WORD hTm);
FARPROC fpTimerProc=NULL;
WORD hTimer=NULL;
char szAppName[]="SystemTimer";
int PASCAL WinMain(HANDLE hInstance,HANDLE hPrevInstance,
LPSTR lpszCmdParam,int nCmdShow)
{
WNDCLASS wc;
HWND hWndMain;
int i,j;
HCURSOR hcurSave;
if(hPrevInstance=NULL){
wc.lpszMenuName =NULL;
wc.lpszClassName =szAppName;
wc.hInstance =hInstance;
wc.hIcon =LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor =LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground =(HBRUSH)COLOR_WINDOW+1;
wc.style =0;
wc.lpfnWndproc =DefWindowProc;
wc.cbClsExtra =0;
wc.cbWndExtra =0;
if(!RegisterClass(&wc))
return(0);
}
if(hWndMain=CreateWindow(szAppName,
szAppName,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
NULL,NULL,hInstance,NULL))=NULL)
return(0);
ShowWindow(hWndmain,nCmdShow);
Update Window(hWndMain);
fpTimerProc=MakeProcInstance((FARPROC)MyTimerProc,hInstance);
if(hTimer=SetUpSystemTimer(1000))=NULL{
MessageBox(hwndMain,"Set System Timer Error",
szAppName,MB_ICONEXCLAMATION:MB_OK);
return 0;
}
hcurSave=SetCursor(LoadCursor(NULL,IDC_WAIT));
for(i=0;i<10000;i++)
for(j=0;j<10000;j++)
SetCursor(hcurSave);
ClearSystemTime(hTimer);
}
WORD SetUpSystemTime(WORD wTimeOut)
{
WORD hTm;
if((hTm=CreateSystemTimer(wTimeOut,fpTimerProc))=NULL){
fpTimerProc=NULL:
return NULL
}else return hTm;
}
BOOL ClearSystemTimer(WORD hTm)
{
if(hTm){
if(KillSystemTimer(hTm)!=0)
return FALSE;
hTm=NULL;
}
return TRUE
voidFAR PASCAL_export MyTimerProc(void)
{
MessageBeep(0);
}
////////////////////////////////////
//SystemT.def
NAME SystemTimer
DESCRIPTION`System Timer`
EXETYPE WINDOWS
STUB `WINSTUB`
CODE PRELOAD
DATA PRELOAD MOVABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS MyTimerProc
IMPORTS CreateSystemTimer=SYSTEM.CREATESYSTEMTIMER
KillSystemTimer=SYSTEM.KILLSYSTEMTIMER
toyee 2001-08-01
  • 打赏
  • 举报
回复
我推荐一个精确到微秒级的函数
QueryPerformanceFrequency()
QueryPerformanceCounter()

第一个函数返回PC机8253芯片的FCLK,这个值约1.19MHz,也就是说一秒钟可发出这么多的计时信号,它的倒数约840ns,这是定时器的极限精度了。
第二个函数返回定时信号个数,差一下,再乘840ns,就是时间了。
ldghj1 2001-08-01
  • 打赏
  • 举报
回复
API有取当前系统时间的函数,精确到毫秒,可以看看MSDN,我用系统时钟做实时系统,非常精确的
五行擒拿术 2001-08-01
  • 打赏
  • 举报
回复
Ticket是取得你应用程序运行的时间。。
土豆 2001-08-01
  • 打赏
  • 举报
回复
有啊,Ticket
五行擒拿术 2001-08-01
  • 打赏
  • 举报
回复
好像有个多媒体时钟,可以精确到1毫秒
c_z_y 2001-08-01
  • 打赏
  • 举报
回复
使用Time是有几十毫秒的底线误差的。
如果是动画方面,一般可以使用另外的线程,timeGetTime来得到毫秒级时间,然后决定是否
frame move。

如果不是,你也可以试试这个
MMRESULT timeSetEvent(
UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
DWORD dwUser,
UINT fuEvent
);
young_t 2001-08-01
  • 打赏
  • 举报
回复
其实,上面的文章是我以前在一个什么网站找到的,我还没有用过。如果要用到比较精确的定时器,我一般都是用的定时器内核对象,CreateWaitableTimer创建一个定时器内核对象,然后用SetWaitableTimer设置定时器的开始时间,时间间隔什么的。这样的定时器和Sleep应该是同一级别的。
neogeo60b 2001-08-01
  • 打赏
  • 举报
回复
..............?
Kevin_qing 2001-08-01
  • 打赏
  • 举报
回复
瞧瞧
neogeo60b 2001-08-01
  • 打赏
  • 举报
回复
#include "mmsystem.h"
void CVkeyDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
timeBeginPeriod(1);

int start = timeGetTime();
while(timeGetTime() - start < 40)
{

}
timeEndPeriod(1);
CDialog::OnLButtonDown(nFlags, point);
}
编译时出错:
unresolved external symbol __imp__timeEndPeriod@4
unresolved external symbol __imp__timeGetTime@0
unresolved external symbol __imp__timeBeginPeriod@4

16,551

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Creator Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧