delphi的定时器制作计划任务不准确

时间:2022-06-01 21:04:08
真的很急..定时器老是不准确或者有时候跳过定时的任务

作了一个小程序想让他在特定的时间跳出一对话框提醒我该做什么了!!

当程序执行时会从程序目录里的计划列表.txt一行一行读取任务列表定时的时间到
一个数组里,然后让delphi自带的time控件每一秒都查询一次这个数组看有无到时间的
任务,如果有就执行!!

可是这个程序做好后在主界面里看到时间一秒一秒的跳...运行很正常

当打开很多网页或者运行一些其他的大程序的时候..时间就不是一秒一秒的跳了
而时不时隔过去一秒..这样..就会出现有些任务不执行.跳过去了!!

查了半天 看到一些说  多媒体定时器控件  我没有找到!!

请各位帮忙想想办法..该怎么做..真的很急....
如有多媒体定时器控件请发一份给我..谢谢.................
huxhuy@163.net  
小弟还用ThreadedTimer使用多线程作...还是不行....急急急......

20 个解决方案

#1


你判断时间别用等于,用大于不就行了,或者限定在某个时间段内。

单CPU多任务本来就是一个串行(轮循)的过程,如果碰巧某个时间片没分配给你的程序,而这个时间正好是你设定的时间,那不就完蛋了。这个问题即使用多线程也没用。

#2


改为大于也不行..因为有很多任务..而且是从早上到晚上的!!

如果我中午开机..那...大于当前时间的任务就多了!!!

有没有其他方法..可以让时间不跳过某一秒..  

#3


帮你顶。
关注

#4


你判断时间别用等于,用大于不就行了,或者限定在某个时间段内。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
没让你教条的大于,而是让你限定一个时间段,比如大于限定时间但小于1分钟。

提示过的任务可以标示一下,下次不再提示。

想不跳过某一秒不太现实,比如你读光盘的时候,如果碟子不好,系统中IRQ大于它的都没办法响应的。

#5


还是不行呀!!怎样限定时间呀??比如17:50:00 - 17:50:10 = 00:00:10  

而17:50:00 - 17:49:50 = 00:00:10   怎么办???

谁有多媒体定时器控件呀!!可否发一份给我????? 

#6


正负你分不出来呀!

#7


up

#8


pazee(耙子)(灵感点亮生活) 的是正解!

#9


不好意思!!!  

procedure TForm1.Button2Click(Sender: TObject);
var
a,b,c,d:ttime;
begin
  a:=strtotime(edit1.Text);
  b:=strtotime(edit2.Text);
    c:=strtotime(edit1.Text);
  d:=strtotime(edit2.Text);
  a:=a-b;
  c:=d-c;
  showmessage(timetostr(a));
  showmessage(timetostr(c));

end;

没有正负呀!!!   我是用delphi7

#10


showmessage(floatToStr(a*60*60*24));
  showmessage(floatToStr(c*60*60*24));

显示相差的秒数。

#11


楼上的几位“同志”好像对楼主说的多媒体计时器避而不谈。

#12


好像有本书《 Delphi 深度历险 》中有很好的例子。可惜我在外出差资料在公司。

#13


利用delphi建立精确计数器


作者 : TechnoFantasy
在windows中的很多场合下编程(例如工业控制、游戏)中需要比较精确的记时器,本文讨论的是在delphi下实现记时器的若干方法以及它们的精度控制问题。

在delphi中最常用的是timer控件,它的设置和使用都非常方便,理论上它的记时精度可以达到1ms(毫秒)。但是众所周知的,实际上timer在记时间隔小于50ms之下是精度是十分差的。它只适用于对于精度要求不太高的场合。

  这里作者要介绍的是两种利用windows api函数实现精确记时的方法。第一中方法是利用高性能频率记数(作者本人的称呼)法。利用这种方法要使用两个api函数queryperformancefrequency和queryperformancecounter。queryperformancefrequency函数获得高性能频率记数器的震荡频率。

调用该函数后,函数会将系统频率记数器的震荡频率(每毫秒)保存到一个largeinteger中。不过利用该函数在几台机器上做过试验,结果都是1193180。读者朋友可以在自己的机器上试一下

queryperformancecounter函数获得系统频率记数器的震荡次数,结果也保存到一个largenteger中。

很显然,如果在计时中首先使用queryperformancefrequency获得高性能频率记数器每毫秒的震荡次数,然后在计时开始时使用queryperformancecounter函数获得当前系统频率记数器的震荡次数。在计时结束时再调用queryperformancecounter函数获得系统频率记数器的震荡次数。将两者相减,再将结果除以频率记数器每毫秒的震荡次数,就可以获得某一事件经过的准确时间。(次数除以频率等于时间)

另外的一种精确记时器的功能是利用多媒体记时器函数(这也是作者的定义,因为这个系列的函数是在winmm.dll中定义并且是为媒体播放服务的)。

实现多媒体记时器首先要使用timesetevent函数建立计时事件。该函数在delphi中的mmsystem.pas中有定义,定义如下:

function timesetevent(udelay, uresolution: uint;

  lpfunction: tfntimecallback; dwuser: dword; uflags: uint): mmresult; stdcall

函数定义中参数udelay定义延迟时间,以毫秒为单位,该参数相当于timer控件的interval属性。参数uresolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;参数lpfunction定义了timesetevent函数的回调函数。该函数相当于一个定时中断处理函数,每当经过一个udelay长度的时间间隔,该函数就会被调用,编程者可以在该函数中加入相应的处理语句。参数dwuser定义用户自定义的回调值,该值将传递给回调函数。参数uflags定义定时类型,如果要不间断的记时,该值应设置为1。

如果函数调用成功,在系统中建立了一个多媒体记时器对象,每当经过一个udelay时间后lpfunction指定的函数都会被调用。同时函数返回一个对象标识,如果不再需要记时器则必须要使用timekillevent函数删除记时器对象。

 

由于windows是一个多任务的操作系统,因此基于api调用的记时器的精度都会受到其它很多因素的干扰。到底这两中记时器的精度如何,我们来使用以下的程序进行验证:

设置三种记时器(timer控件、高性能频率记数、多媒体记时器)。将它们的定时间隔设置为10毫秒,让它们不停工作直到达到一个比较长的时间(比如60秒),这样记时器的误差会被累计下来,然后同实际经过的时间相比较,就可以得到它们的精度。

下面是具体的检测程序。

unit unit1;

 

interface

 

uses

  windows, messages, sysutils, classes, graphics, controls, forms, dialogs,

  stdctrls, extctrls,mmsystem;

 

type

  tform1 = class(tform)

  edit1: tedit;

  edit2: tedit;

  edit3: tedit;

  button1: tbutton;

  button2: tbutton;

  timer1: ttimer;

  procedure formcreate(sender: tobject);

  procedure button1click(sender: tobject);

  procedure timer1timer(sender: tobject);

  procedure button2click(sender: tobject);

  private

  { private declarations }

  public

  { public declarations }

  end;

 

var

  form1: tform1;

  acttime1,acttime2:cardinal;

  smmcount,stimercount,spcount:single;

  htimeid:integer;

  iten:integer;

  protimecallback:tfntimecallback;

 

procedure timeproc(utimerid, umessage: uint;

  dwuser, dw1, dw2: dword) stdcall;

procedure proendcount;

implementation

 

{$r *.dfm}

//timesetevent的回调函数

procedure proendcount;

begin

  acttime2:=gettickcount-acttime1;

  form1.button2.enabled :=false;

  form1.button1.enabled :=true;

  form1.timer1.enabled :=false;

  smmcount:=60;

  stimercount:=60;

  spcount:=-1;

 

  timekillevent(htimeid);

end;

 

procedure timeproc(utimerid, umessage: uint;

  dwuser, dw1, dw2: dword) stdcall;

begin

  form1.edit2.text:=floattostr(smmcount);

  smmcount:=smmcount-0.01;

end;

 

procedure tform1.formcreate(sender: tobject);

begin

  button1.caption :='开始倒计时';

  button2.caption :='结束倒计时';

  button2.enabled :=false;

  button1.enabled :=true;

  timer1.enabled :=false;

  smmcount:=60;

  stimercount:=60;

  spcount:=60;

end;

 

procedure tform1.button1click(sender: tobject);

var

  lgtick1,lgtick2,lgper:tlargeinteger;

  ftemp:single;

begin

  button2.enabled :=true;

  button1.enabled :=false;

  timer1.enabled :=true;

  timer1.interval :=10;

  protimecallback:=timeproc;

  htimeid:=timesetevent(10,0,protimecallback,1,1);

  acttime1:=gettickcount;

 

  //获得系统的高性能频率计数器在一毫秒内的震动次数

  queryperformancefrequency(lgper);

  ftemp:=lgper/1000;

  iten:=trunc(ftemp*10);

  queryperformancecounter(lgtick1);

  lgtick2:=lgtick1;

  spcount:=60;

  while spcount>0 do begin

  queryperformancecounter(lgtick2);

  //如果时钟震动次数超过10毫秒的次数则刷新edit3的显示

  if lgtick2 - lgtick1 > iten then begin

  lgtick1 := lgtick2;

  spcount := spcount - 0.01;

  edit3.text := floattostr(spcount);

  application.processmessages;

  end;

  end;

end;

 

procedure tform1.timer1timer(sender: tobject);

begin

  edit1.text := floattostr(stimercount);

  stimercount:=stimercount-0.01;

end;

 

procedure tform1.button2click(sender: tobject);

begin

  proendcount;

  //显示从开始记数到记数实际经过的时间

  showmessage('实际经过时间'+inttostr(acttime2)+'毫秒');

end;

 

end.

#14


运行程序,点击“开始倒记时”按钮,程序开始60秒倒记时,由于上面的程序只涉及了记时器程序的原理而没有将错误处理加入其中,所以不要等60秒倒记时结束。点击“结束倒记时”按钮可以结束倒记时。这时在弹出对话框中会显示实际经过的时间(单位为毫秒),将三个文本框内的时间乘以1000再加上实际经过的时间,越接近60000,则记时精度越高。

下面是在我的机器上的执行结果。


  从上面的结果看,由delphi的timer控件建立的记时器的精度十分差,无法在实际中使用,而利用高性能频率记数法和多媒体计数器法的误差都在1%以下。考虑到程序中在文本框中显示时间对程序所造成的影响,这个误差在应用中是完全可以忽略的。

另外在运行程序时作者还发现一个问题,如果在倒记时时拖动窗口,文本框中的显示都会停止,而当停止窗口拖放后,多媒体记时器显示会跳过这段时间记时,而其它两种记时器显示倒记时却还是从原来的时间倒数。这说明多媒体记时器是在独立的线程中运行的,不会受到程序的影响。

综合上面的介绍和范例,我们可以看到,如果要建立高精度的记时器,使用多媒体记时器是比较好的选择。而高性能频率记数法比较适合计算某个耗时十分短的过程所消耗的时间(例如分析程序中某个被多次调用的程序段执行时间以优化程序),因为毕竟高性能频率记数的理论可以达到微秒级别。timer控件虽然精度比上面两者差很多,但是它使用方便,在要求不高的场合它还是最佳选择。

(最后要说的是,以上的结果都是在windows 9x下获得的,作者在windows 2000下运行该程序时发现,timer控件的精度比在windows 9x下要高出很多,一般误差在5%以下,这说明windows 2000是一个真正的多任务操作系统。再加上windows nt\2000的稳定性和易用性,在工业控制或实时检测等领域是一个比较完美的平台)

#15


多媒体计时器的优先级最高。

#16


---- 在工业控制软件中,数据采集和实时控制是经常性的工作。目前,工业控制软件已经从DOS和Windows3.x平台,转到了Windows95上。然而,Windows95并不能直接支持中断,这就意味着在DOS和Windows3.x中的定时器中断不能为我们所用。因此,我们必须寻求新的定时方式。
---- 在C++ Builder和Delphi等典型的编程语言中,都提供了定时器控件,可以方便地实现定时和事件响应。此外,Windows95还提供了SetTimer和KillTimer函数来设置和删除一个定时器,在事件WM_TIMER响应函数中实现处理。然而,遗憾的是,通过这些方式获得的Win95定时器最小只能精确到55毫秒,对于55毫秒以下的时间精度便无能为力。这对于Win95下测控软件的开发是十分不利的。幸运的是,多媒体定时器可以解决这一难题。
---- 以下是一个完整的多媒体定时器设计示例程序,该定时器的精度为1毫秒。该程序的内容十分简单:按下按钮1,启动定时器,在Edit框中显示定时值(计数值),按下按钮2则将关闭定时器。此程序在Windows95环境下,采用C++Builder3.0编程并编译通过,但设计思想同样适用于其它高级语言。按照本程序中的实现方法,读者将可以轻松实现自己的高精度定时器。
---- 程序清单如下:


#include < vcl.h >#pragma hdrstop#include "mmsystem.h"   //包含多媒体定时器函数的头文件#define MilliSecond  1  //定时间隔1毫秒#define Accuracy     1  //系统允许的分辨率最小值#define Min(x,y)  ((x < y) ? x : y)#define Max(x,y)  ((x > y) ? x : y)#include "HighTimerU.h"//------------------------------------------------#pragma package(smart_init)#pragma resource "*.dfm"UINT TimerID;    //定义定时器句柄int count;       //定义一个变量以进行计数int TimerAccuracy;TForm1 *Form1;//------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)    : TForm(Owner){}void PASCAL TimerCallProc(UINT  TimerID, UINT msg,DWORD dwUser,           DWORD dwa,DWORD dwb)  //定义定时器事件的调用函数{    count++;    Form1- >Edit1->Text=count;    //在一个编辑框内显示计数值,即定时值}//---------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender){    TIMECAPS timecaps;    int TimerResolution; //从系统获得关于定时器服务能力的信息,//分辨率不能超出系统许可值(1到16毫秒)  if (timeGetDevCaps(&timecaps,sizeof(TIMECAPS))==TIMERR_NOERROR)      TimerAccuracy=Min(Max(timecaps.wPeriodMin,Accuracy),timecaps.wPeriodMax);    timeBeginPeriod(TimerAccuracy);  //设置定时器分辨率      TimerResolution=1;    //设置定时间隔为1毫秒   //产生间隔1毫秒,周期执行的定时器事件;启动定时器    TimerID = timeSetEvent(TimerResolution,TimerAccuracy,                        &TimerCallProc,1,TIME_PERIODIC);}//-------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender){    timeKillEvent(TimerID);        //删除定时器事件    timeEndPeriod(TimerAccuracy);  //清除定时器分辨率}
---- 多媒体定时器对实现高精度定时是很理想的工具,而且其精度是十分可靠的。但是,多媒体定时器也并不是完美的。因为它可靠的精度是建立在对系统资源的消耗之上的。因此,在利用多媒体定时器完成工作的同时,必须注意以下几点:
---- 1.多媒体定时器的设置分辨率不能超出系统许可范围。
---- 2.在使用完定时器以后,一定要及时删除定时器及其分辨率,否则系统会越来越慢。
---- 3.多媒体定时器在启动时,将自动开辟一个独立的线程。在定时器线程结束之前,注意一定不能再次启动该定时器,不然将迅速造成死机

#17


1.多媒体定时器的设置分辨率不能超出系统许可范围。
对啊 那你怎么说:参数uresolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;
系统会被宕掉!

#18


to  readersm68(地主)
我们不是对于多媒体定时器避而不谈,而是对于楼主的需求用多媒体定时器完全没必要。它不需要那么高的精度,他精确到秒足够了。
现在他提出的问题不是精不精确,而是如果某个进程占用大量的时间片,它的程序可能得不到相应。

对于上述的需求,提高时钟的精度对于成本来说是不合理的。

#19


学习!

#20


解决了!!

#1


你判断时间别用等于,用大于不就行了,或者限定在某个时间段内。

单CPU多任务本来就是一个串行(轮循)的过程,如果碰巧某个时间片没分配给你的程序,而这个时间正好是你设定的时间,那不就完蛋了。这个问题即使用多线程也没用。

#2


改为大于也不行..因为有很多任务..而且是从早上到晚上的!!

如果我中午开机..那...大于当前时间的任务就多了!!!

有没有其他方法..可以让时间不跳过某一秒..  

#3


帮你顶。
关注

#4


你判断时间别用等于,用大于不就行了,或者限定在某个时间段内。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
没让你教条的大于,而是让你限定一个时间段,比如大于限定时间但小于1分钟。

提示过的任务可以标示一下,下次不再提示。

想不跳过某一秒不太现实,比如你读光盘的时候,如果碟子不好,系统中IRQ大于它的都没办法响应的。

#5


还是不行呀!!怎样限定时间呀??比如17:50:00 - 17:50:10 = 00:00:10  

而17:50:00 - 17:49:50 = 00:00:10   怎么办???

谁有多媒体定时器控件呀!!可否发一份给我????? 

#6


正负你分不出来呀!

#7


up

#8


pazee(耙子)(灵感点亮生活) 的是正解!

#9


不好意思!!!  

procedure TForm1.Button2Click(Sender: TObject);
var
a,b,c,d:ttime;
begin
  a:=strtotime(edit1.Text);
  b:=strtotime(edit2.Text);
    c:=strtotime(edit1.Text);
  d:=strtotime(edit2.Text);
  a:=a-b;
  c:=d-c;
  showmessage(timetostr(a));
  showmessage(timetostr(c));

end;

没有正负呀!!!   我是用delphi7

#10


showmessage(floatToStr(a*60*60*24));
  showmessage(floatToStr(c*60*60*24));

显示相差的秒数。

#11


楼上的几位“同志”好像对楼主说的多媒体计时器避而不谈。

#12


好像有本书《 Delphi 深度历险 》中有很好的例子。可惜我在外出差资料在公司。

#13


利用delphi建立精确计数器


作者 : TechnoFantasy
在windows中的很多场合下编程(例如工业控制、游戏)中需要比较精确的记时器,本文讨论的是在delphi下实现记时器的若干方法以及它们的精度控制问题。

在delphi中最常用的是timer控件,它的设置和使用都非常方便,理论上它的记时精度可以达到1ms(毫秒)。但是众所周知的,实际上timer在记时间隔小于50ms之下是精度是十分差的。它只适用于对于精度要求不太高的场合。

  这里作者要介绍的是两种利用windows api函数实现精确记时的方法。第一中方法是利用高性能频率记数(作者本人的称呼)法。利用这种方法要使用两个api函数queryperformancefrequency和queryperformancecounter。queryperformancefrequency函数获得高性能频率记数器的震荡频率。

调用该函数后,函数会将系统频率记数器的震荡频率(每毫秒)保存到一个largeinteger中。不过利用该函数在几台机器上做过试验,结果都是1193180。读者朋友可以在自己的机器上试一下

queryperformancecounter函数获得系统频率记数器的震荡次数,结果也保存到一个largenteger中。

很显然,如果在计时中首先使用queryperformancefrequency获得高性能频率记数器每毫秒的震荡次数,然后在计时开始时使用queryperformancecounter函数获得当前系统频率记数器的震荡次数。在计时结束时再调用queryperformancecounter函数获得系统频率记数器的震荡次数。将两者相减,再将结果除以频率记数器每毫秒的震荡次数,就可以获得某一事件经过的准确时间。(次数除以频率等于时间)

另外的一种精确记时器的功能是利用多媒体记时器函数(这也是作者的定义,因为这个系列的函数是在winmm.dll中定义并且是为媒体播放服务的)。

实现多媒体记时器首先要使用timesetevent函数建立计时事件。该函数在delphi中的mmsystem.pas中有定义,定义如下:

function timesetevent(udelay, uresolution: uint;

  lpfunction: tfntimecallback; dwuser: dword; uflags: uint): mmresult; stdcall

函数定义中参数udelay定义延迟时间,以毫秒为单位,该参数相当于timer控件的interval属性。参数uresolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;参数lpfunction定义了timesetevent函数的回调函数。该函数相当于一个定时中断处理函数,每当经过一个udelay长度的时间间隔,该函数就会被调用,编程者可以在该函数中加入相应的处理语句。参数dwuser定义用户自定义的回调值,该值将传递给回调函数。参数uflags定义定时类型,如果要不间断的记时,该值应设置为1。

如果函数调用成功,在系统中建立了一个多媒体记时器对象,每当经过一个udelay时间后lpfunction指定的函数都会被调用。同时函数返回一个对象标识,如果不再需要记时器则必须要使用timekillevent函数删除记时器对象。

 

由于windows是一个多任务的操作系统,因此基于api调用的记时器的精度都会受到其它很多因素的干扰。到底这两中记时器的精度如何,我们来使用以下的程序进行验证:

设置三种记时器(timer控件、高性能频率记数、多媒体记时器)。将它们的定时间隔设置为10毫秒,让它们不停工作直到达到一个比较长的时间(比如60秒),这样记时器的误差会被累计下来,然后同实际经过的时间相比较,就可以得到它们的精度。

下面是具体的检测程序。

unit unit1;

 

interface

 

uses

  windows, messages, sysutils, classes, graphics, controls, forms, dialogs,

  stdctrls, extctrls,mmsystem;

 

type

  tform1 = class(tform)

  edit1: tedit;

  edit2: tedit;

  edit3: tedit;

  button1: tbutton;

  button2: tbutton;

  timer1: ttimer;

  procedure formcreate(sender: tobject);

  procedure button1click(sender: tobject);

  procedure timer1timer(sender: tobject);

  procedure button2click(sender: tobject);

  private

  { private declarations }

  public

  { public declarations }

  end;

 

var

  form1: tform1;

  acttime1,acttime2:cardinal;

  smmcount,stimercount,spcount:single;

  htimeid:integer;

  iten:integer;

  protimecallback:tfntimecallback;

 

procedure timeproc(utimerid, umessage: uint;

  dwuser, dw1, dw2: dword) stdcall;

procedure proendcount;

implementation

 

{$r *.dfm}

//timesetevent的回调函数

procedure proendcount;

begin

  acttime2:=gettickcount-acttime1;

  form1.button2.enabled :=false;

  form1.button1.enabled :=true;

  form1.timer1.enabled :=false;

  smmcount:=60;

  stimercount:=60;

  spcount:=-1;

 

  timekillevent(htimeid);

end;

 

procedure timeproc(utimerid, umessage: uint;

  dwuser, dw1, dw2: dword) stdcall;

begin

  form1.edit2.text:=floattostr(smmcount);

  smmcount:=smmcount-0.01;

end;

 

procedure tform1.formcreate(sender: tobject);

begin

  button1.caption :='开始倒计时';

  button2.caption :='结束倒计时';

  button2.enabled :=false;

  button1.enabled :=true;

  timer1.enabled :=false;

  smmcount:=60;

  stimercount:=60;

  spcount:=60;

end;

 

procedure tform1.button1click(sender: tobject);

var

  lgtick1,lgtick2,lgper:tlargeinteger;

  ftemp:single;

begin

  button2.enabled :=true;

  button1.enabled :=false;

  timer1.enabled :=true;

  timer1.interval :=10;

  protimecallback:=timeproc;

  htimeid:=timesetevent(10,0,protimecallback,1,1);

  acttime1:=gettickcount;

 

  //获得系统的高性能频率计数器在一毫秒内的震动次数

  queryperformancefrequency(lgper);

  ftemp:=lgper/1000;

  iten:=trunc(ftemp*10);

  queryperformancecounter(lgtick1);

  lgtick2:=lgtick1;

  spcount:=60;

  while spcount>0 do begin

  queryperformancecounter(lgtick2);

  //如果时钟震动次数超过10毫秒的次数则刷新edit3的显示

  if lgtick2 - lgtick1 > iten then begin

  lgtick1 := lgtick2;

  spcount := spcount - 0.01;

  edit3.text := floattostr(spcount);

  application.processmessages;

  end;

  end;

end;

 

procedure tform1.timer1timer(sender: tobject);

begin

  edit1.text := floattostr(stimercount);

  stimercount:=stimercount-0.01;

end;

 

procedure tform1.button2click(sender: tobject);

begin

  proendcount;

  //显示从开始记数到记数实际经过的时间

  showmessage('实际经过时间'+inttostr(acttime2)+'毫秒');

end;

 

end.

#14


运行程序,点击“开始倒记时”按钮,程序开始60秒倒记时,由于上面的程序只涉及了记时器程序的原理而没有将错误处理加入其中,所以不要等60秒倒记时结束。点击“结束倒记时”按钮可以结束倒记时。这时在弹出对话框中会显示实际经过的时间(单位为毫秒),将三个文本框内的时间乘以1000再加上实际经过的时间,越接近60000,则记时精度越高。

下面是在我的机器上的执行结果。


  从上面的结果看,由delphi的timer控件建立的记时器的精度十分差,无法在实际中使用,而利用高性能频率记数法和多媒体计数器法的误差都在1%以下。考虑到程序中在文本框中显示时间对程序所造成的影响,这个误差在应用中是完全可以忽略的。

另外在运行程序时作者还发现一个问题,如果在倒记时时拖动窗口,文本框中的显示都会停止,而当停止窗口拖放后,多媒体记时器显示会跳过这段时间记时,而其它两种记时器显示倒记时却还是从原来的时间倒数。这说明多媒体记时器是在独立的线程中运行的,不会受到程序的影响。

综合上面的介绍和范例,我们可以看到,如果要建立高精度的记时器,使用多媒体记时器是比较好的选择。而高性能频率记数法比较适合计算某个耗时十分短的过程所消耗的时间(例如分析程序中某个被多次调用的程序段执行时间以优化程序),因为毕竟高性能频率记数的理论可以达到微秒级别。timer控件虽然精度比上面两者差很多,但是它使用方便,在要求不高的场合它还是最佳选择。

(最后要说的是,以上的结果都是在windows 9x下获得的,作者在windows 2000下运行该程序时发现,timer控件的精度比在windows 9x下要高出很多,一般误差在5%以下,这说明windows 2000是一个真正的多任务操作系统。再加上windows nt\2000的稳定性和易用性,在工业控制或实时检测等领域是一个比较完美的平台)

#15


多媒体计时器的优先级最高。

#16


---- 在工业控制软件中,数据采集和实时控制是经常性的工作。目前,工业控制软件已经从DOS和Windows3.x平台,转到了Windows95上。然而,Windows95并不能直接支持中断,这就意味着在DOS和Windows3.x中的定时器中断不能为我们所用。因此,我们必须寻求新的定时方式。
---- 在C++ Builder和Delphi等典型的编程语言中,都提供了定时器控件,可以方便地实现定时和事件响应。此外,Windows95还提供了SetTimer和KillTimer函数来设置和删除一个定时器,在事件WM_TIMER响应函数中实现处理。然而,遗憾的是,通过这些方式获得的Win95定时器最小只能精确到55毫秒,对于55毫秒以下的时间精度便无能为力。这对于Win95下测控软件的开发是十分不利的。幸运的是,多媒体定时器可以解决这一难题。
---- 以下是一个完整的多媒体定时器设计示例程序,该定时器的精度为1毫秒。该程序的内容十分简单:按下按钮1,启动定时器,在Edit框中显示定时值(计数值),按下按钮2则将关闭定时器。此程序在Windows95环境下,采用C++Builder3.0编程并编译通过,但设计思想同样适用于其它高级语言。按照本程序中的实现方法,读者将可以轻松实现自己的高精度定时器。
---- 程序清单如下:


#include < vcl.h >#pragma hdrstop#include "mmsystem.h"   //包含多媒体定时器函数的头文件#define MilliSecond  1  //定时间隔1毫秒#define Accuracy     1  //系统允许的分辨率最小值#define Min(x,y)  ((x < y) ? x : y)#define Max(x,y)  ((x > y) ? x : y)#include "HighTimerU.h"//------------------------------------------------#pragma package(smart_init)#pragma resource "*.dfm"UINT TimerID;    //定义定时器句柄int count;       //定义一个变量以进行计数int TimerAccuracy;TForm1 *Form1;//------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)    : TForm(Owner){}void PASCAL TimerCallProc(UINT  TimerID, UINT msg,DWORD dwUser,           DWORD dwa,DWORD dwb)  //定义定时器事件的调用函数{    count++;    Form1- >Edit1->Text=count;    //在一个编辑框内显示计数值,即定时值}//---------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender){    TIMECAPS timecaps;    int TimerResolution; //从系统获得关于定时器服务能力的信息,//分辨率不能超出系统许可值(1到16毫秒)  if (timeGetDevCaps(&timecaps,sizeof(TIMECAPS))==TIMERR_NOERROR)      TimerAccuracy=Min(Max(timecaps.wPeriodMin,Accuracy),timecaps.wPeriodMax);    timeBeginPeriod(TimerAccuracy);  //设置定时器分辨率      TimerResolution=1;    //设置定时间隔为1毫秒   //产生间隔1毫秒,周期执行的定时器事件;启动定时器    TimerID = timeSetEvent(TimerResolution,TimerAccuracy,                        &TimerCallProc,1,TIME_PERIODIC);}//-------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender){    timeKillEvent(TimerID);        //删除定时器事件    timeEndPeriod(TimerAccuracy);  //清除定时器分辨率}
---- 多媒体定时器对实现高精度定时是很理想的工具,而且其精度是十分可靠的。但是,多媒体定时器也并不是完美的。因为它可靠的精度是建立在对系统资源的消耗之上的。因此,在利用多媒体定时器完成工作的同时,必须注意以下几点:
---- 1.多媒体定时器的设置分辨率不能超出系统许可范围。
---- 2.在使用完定时器以后,一定要及时删除定时器及其分辨率,否则系统会越来越慢。
---- 3.多媒体定时器在启动时,将自动开辟一个独立的线程。在定时器线程结束之前,注意一定不能再次启动该定时器,不然将迅速造成死机

#17


1.多媒体定时器的设置分辨率不能超出系统许可范围。
对啊 那你怎么说:参数uresolution定义记时精度,如果要求尽可能高的精度,要将该参数设置为0;
系统会被宕掉!

#18


to  readersm68(地主)
我们不是对于多媒体定时器避而不谈,而是对于楼主的需求用多媒体定时器完全没必要。它不需要那么高的精度,他精确到秒足够了。
现在他提出的问题不是精不精确,而是如果某个进程占用大量的时间片,它的程序可能得不到相应。

对于上述的需求,提高时钟的精度对于成本来说是不合理的。

#19


学习!

#20


解决了!!

#21