专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Delphi教程 » delphi计时器:Delphi建立精确计时器 »正文

delphi计时器:Delphi建立精确计时器

来源: 发布时间:星期四, 2009年2月12日 浏览:82次 评论:0


利用Delphi建立精确计数器

在Windows中很多场合下编程(例如工业控制、游戏)中需要比较精确记时器本文讨论是在Delphi下实现记时器若干思路方法以及它们精度控制问题
在Delphi中最常用是TimerControl控件设置和使用都非常方便理论上它记时精度可以达到1ms(毫秒)但是众所周知实际上Timer在记时间隔小于50ms的下是精度是十分差它只适用于对于精度要求不太高场合
这里作者要介绍是两种利用Windows API实现精确记时思路方法中思路方法是利用高性能频率记数(作者本人称呼)法利用这种思路方法要使用两个APIQueryPerformanceFrequency和QueryPerformanceCounterQueryPerformanceFrequency获得高性能频率记数器震荡频率
会将系统频率记数器震荡频率(每毫秒)保存到个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定义延迟时间以毫秒为单位该参数相当于TimerControl控件Interval属性参数uResolution定义记时精度如果要求尽可能高精度要将该参数设置为0;参数lpFunction定义了timeSetEvent回调相当于个定时中断处理每当经过个uDelay长度时间间隔就会被编程者可以在该中加入相应处理语句参数dwUser定义用户自定义回调值该值将传递给回调参数uFlags定义定时类型如果要不间断记时该值应设置为1
如果成功在系统中建立了个多媒体记时器对象每当经过个uDelay时间后lpFunction指定都会被同时返回个对象标识如果不再需要记时器则必须要使用timeKillEvent删除记时器对象

由于Windows是个多任务操作系统因此基于API记时器精度都会受到其它很多原因干扰到底这两中记时器精度如何我们来使用以下进行验证:
设置 3种记时器(TimerControl控件、高性能频率记数、多媒体记时器)将它们定时间隔设置为10毫秒让它们不停工作直到达到个比较长时间(比如60秒)这样记时器误差会被累计下来然后同实际经过时间相比较就可以得到它们精度
下面是具体检测
unit Unit1;

erface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls,mm;

type
TForm1 = (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.
运行点击“开始倒记时”按钮开始60秒倒记时由于上面只涉及了记时器原理而没有将处理加入其中所以不要等60秒倒记时结束点击“结束倒记时”按钮可以结束倒记时这时在弹出对话框中会显示实际经过时间(单位为毫秒)将 3个文本框内时间乘以1000再加上实际经过时间越接近60000则记时精度越高
下面是在我机器上执行结果

从上面结果看由DelphiTimerControl控件建立记时器精度十分差无法在实际中使用而利用高性能频率记数法和多媒体计数器法误差都在1%以下考虑到中在文本框中显示时间对所造成影响这个误差在应用中是完全可以忽略
另外在运行时作者还发现个问题如果在倒记时时拖动窗口文本框中显示都会停止而当停止窗口拖放后多媒体记时器显示会跳过这段时间记时而其它两种记时器显示倒记时却还是从原来时间倒数这介绍说明多媒体记时器是在独立线程中运行不会受到影响
综合上面介绍和范例我们可以看到如果要建立高精度记时器使用多媒体记时器是比较好选择而高性能频率记数法比较适合计算某个耗时十分短过程所消耗时间(例如分析中某个被多次段执行时间以优化)毕竟高性能频率记数理论可以达到微秒级别TimerControl控件虽然精度比上面两者差很多但是它使用方便在要求不高场合它还是最佳选择
(最后要说以上结果都是在Windows 9X下获得作者在Windows 2000下运行该时发现TimerControl控件精度比在Windows 9X下要高出很多般误差在5%以下这介绍说明Windows 2000是个真正多任务操作系统再加上Windows NT\\2000稳定性和易用性在工业控制或实时检测等领域是个比较完美平台)
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: