具体如何根据硬件的时钟滴答来发送TIMER消息,我不太了解,我只是说一下WINDOWS
对它的处理,WM_TIMER在消息队列中的优先级基本是最低的,并且在一个队列中同时只能有
一个timer消息。比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次(这里假设你的程序中没有其他事件要处理,只有一个timer事件)。
举个例子,你在窗口中放一个timer组件和一个memo组件,timer一秒触发一次,然后在timer事件中加入这段代码:
sleep(1250);
memo1.lines.add(datetimetostr(now()));
那么你会发现memo中是按照这个规律显示时间:
2005-3-24 21:22:31
2005-3-24 21:22:32
2005-3-24 21:22:33
2005-3-24 21:22:35
2005-3-24 21:22:36
2005-3-24 21:22:37
2005-3-24 21:22:38
2005-3-24 21:22:40
2005-3-24 21:22:41
2005-3-24 21:22:42
2005-3-24 21:22:43
2005-3-24 21:22:45
2005-3-24 21:22:46
2005-3-24 21:22:47
2005-3-24 21:22:48
2005-3-24 21:22:50
2005-3-24 21:22:51
2005-3-24 21:22:52
2005-3-24 21:22:53
2005-3-24 21:22:55
注意一下后面的秒,有一定的规律,如果把时间精确到毫秒的话,那么可以看出其实是每隔
1.25秒显示一次时间。
我有一个疑问,如果你的timer事件的处理需要一定的时间,那么在段时间内,其他新加入队列的消息
就得不到及时的处理,程序看起来象死机一样,此时该怎办??调用processmessages??
34 个解决方案
#1
你中断了1250毫秒之后,得到的当然是间隔1250毫秒之后的时间啊,郁闷
#2
但是你的TIMER是1秒一次,所以造成了这个延时显示
#3
我的意思是如果你想很精确的每一秒钟执行一个事件是不可能的,timer事件并不是异步执行的
#4
關于timer , WM_TIMER的,在 delphi 深度歷險面一書中,說得很清楚
#5
>>如果你的timer事件的处理需要一定的时间,那么在段时间内,其他新加入队列的消息
就得不到及时的处理,程序看起来象死机一样,此时该怎办??调用processmessages??
调用processmessages是一个办法
但是如果处理时间>3秒,我想使用TImer事件就不太好了 还是提交给另一个线程吧
就得不到及时的处理,程序看起来象死机一样,此时该怎办??调用processmessages??
调用processmessages是一个办法
但是如果处理时间>3秒,我想使用TImer事件就不太好了 还是提交给另一个线程吧
#6
因为sleep(1250);的中断等级高于Timer,所以先中断1.25秒再计时,出现这样的问题是当然的。
#7
你可以参考大富翁论坛
关于:"时钟控件定时产生线程操作数据库,shi线程资源释放的问题"
该问题的URL是: http://www.delphibbs.com/delphibbs/dispq.asp?LID=673905
可能会有点启发。
关于:"时钟控件定时产生线程操作数据库,shi线程资源释放的问题"
该问题的URL是: http://www.delphibbs.com/delphibbs/dispq.asp?LID=673905
可能会有点启发。
#8
〉〉我的意思是如果你想很精确的每一秒钟执行一个事件是不可能的,timer事件并不是异步执行的
为啥不sleep(5250);?这样更容易看出问题
为啥不sleep(5250);?这样更容易看出问题
#9
timer在消息队列中的等级是最低的,即便不用sleep,使用其他方法使该timer事件执行时间超过1秒,或者程序中处理其他的消息超过一秒,那么结果也会是1秒钟的timer不会真正的一秒触发一次。
是不是这样??
是不是这样??
#10
还有一个问题,timer的最低分辨率是大概10毫秒,那么如果你的timer间隔是1毫秒的话,也会10毫秒触发一次,有没有其他的办法使计时器能够更加精确??
#11
最新发现,timer事件的触发间隔是完全无规律的,甚至两个timer事件的时间间隔比你定义的还要短,有
程序为证:
定义两个全局变量:
iInterval: integer;
dtBegin: TDateTime;
timer事件如下:
procedure TForm1.Timer1Timer(Sender: TObject);
var
boolFlag: boolean;
dtCur: TDateTime;
h,m,s,ms: Word;
begin
boolFlag := true;
while boolFlag do
begin
iInterval := iInterVal + 1;
dtCur := Now();
DecodeTime((dtCur - dtBegin),h,m,s,ms);
Memo1.Lines.Add(Inttostr(s*1000 + ms));
dtBegin := dtCur;
if iInterval mod 2 = 0 then
Sleep(1600)
else
Sleep(200);
boolFlag := false;
end;
end;
结果的规律如下:
1002
1602
401
1602
401
1602
400
1603
400
1603
400
1603
400
1602
401
1602
401
1602
401
1602
401
1602
这表明我定义1秒触发一次的timer,由于我的timer事件执行时间过长,导致两个timer事件之间间隔
小于1秒
程序为证:
定义两个全局变量:
iInterval: integer;
dtBegin: TDateTime;
timer事件如下:
procedure TForm1.Timer1Timer(Sender: TObject);
var
boolFlag: boolean;
dtCur: TDateTime;
h,m,s,ms: Word;
begin
boolFlag := true;
while boolFlag do
begin
iInterval := iInterVal + 1;
dtCur := Now();
DecodeTime((dtCur - dtBegin),h,m,s,ms);
Memo1.Lines.Add(Inttostr(s*1000 + ms));
dtBegin := dtCur;
if iInterval mod 2 = 0 then
Sleep(1600)
else
Sleep(200);
boolFlag := false;
end;
end;
结果的规律如下:
1002
1602
401
1602
401
1602
400
1603
400
1603
400
1603
400
1602
401
1602
401
1602
401
1602
401
1602
这表明我定义1秒触发一次的timer,由于我的timer事件执行时间过长,导致两个timer事件之间间隔
小于1秒
#12
>>timer在消息队列中的等级是最低的,即便不用sleep,使用其他方法使该timer事件执行时间超过1秒,或者程序中处理其他的消息超过一秒,那么结果也会是1秒钟的timer不会真正的一秒触发一次。
是不是这样??
---肯定的啊
>>有没有其他的办法使计时器能够更加精确??
---多媒体计时器
>>timer事件的触发间隔是完全无规律的。。。甚至两个timer事件的时间间隔比你定义的还要短
---错!
procedure TForm1.Timer1Timer(Sender: TObject);
...
begin
boolFlag := true;
while boolFlag do
begin
...
if iInterval mod 2 = 0 then
Sleep(1600)//你sleep了,所以你得程序不会处理(响应)任何消息了~~~
//但是系统还是按时给你发送了一个timer消息,可是你在睡觉,不响应它,
//当你醒过来处理这个消息的时候,时间已经过去了
//1600-1000=600ms,所以这次的间隔是1600ms;
else
Sleep(200);//你又sleep了,但是系统按时给你发送了一个timer消息,这次的是在你醒来200ms后 //后接到的,所以sleep的200ms+200ms=400ms....一切都是很有规律的啊!!!~
boolFlag := false;
end;
end;
是不是这样??
---肯定的啊
>>有没有其他的办法使计时器能够更加精确??
---多媒体计时器
>>timer事件的触发间隔是完全无规律的。。。甚至两个timer事件的时间间隔比你定义的还要短
---错!
procedure TForm1.Timer1Timer(Sender: TObject);
...
begin
boolFlag := true;
while boolFlag do
begin
...
if iInterval mod 2 = 0 then
Sleep(1600)//你sleep了,所以你得程序不会处理(响应)任何消息了~~~
//但是系统还是按时给你发送了一个timer消息,可是你在睡觉,不响应它,
//当你醒过来处理这个消息的时候,时间已经过去了
//1600-1000=600ms,所以这次的间隔是1600ms;
else
Sleep(200);//你又sleep了,但是系统按时给你发送了一个timer消息,这次的是在你醒来200ms后 //后接到的,所以sleep的200ms+200ms=400ms....一切都是很有规律的啊!!!~
boolFlag := false;
end;
end;
#13
Timer是不准时的!
想可靠就用多媒体定时器
我在FAQ中提过的,http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
http://lysoft.7u7.net
想可靠就用多媒体定时器
我在FAQ中提过的,http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
http://lysoft.7u7.net
#14
TTimer 精确度在 50 - 49 ms 之间 Windows2K 比 Win98 稍准一点
你要实现小于50ms的计时用TTimer是不可能实现滴 楼上多提到的多嫖体定时器应用
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD); stdcall; //计时器回调函数
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD);
begin
PostMessage(dwUser,CM_MARQUEE,dwUser,uTimerID);
end;
procedure TSupperLED.SetHPTimer;
var
pTP: PTimeCaps;
begin
if FTimeID <> 0 then DeleteHPTimer;
GetMem(pTP,SizeOf(TTimeCaps));
try
timeGetDevCaps(pTP,SizeOf(TTimeCaps)); //取当前系统最高计时精度能力
timeBeginPeriod(pTP^.wPeriodMin); //设置计时精度为最佳
finally
FreeMem(pTP,SizeOf(TTimeCaps));
end;
{传入接收消息窗口句柄备用}
FTimeID := timeSetEvent(1000 - FSpeed,1,@TimeCallBack,FWindowHandle,TIME_PERIODIC) //设置高精度计时器
end;
你要实现小于50ms的计时用TTimer是不可能实现滴 楼上多提到的多嫖体定时器应用
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD); stdcall; //计时器回调函数
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD);
begin
PostMessage(dwUser,CM_MARQUEE,dwUser,uTimerID);
end;
procedure TSupperLED.SetHPTimer;
var
pTP: PTimeCaps;
begin
if FTimeID <> 0 then DeleteHPTimer;
GetMem(pTP,SizeOf(TTimeCaps));
try
timeGetDevCaps(pTP,SizeOf(TTimeCaps)); //取当前系统最高计时精度能力
timeBeginPeriod(pTP^.wPeriodMin); //设置计时精度为最佳
finally
FreeMem(pTP,SizeOf(TTimeCaps));
end;
{传入接收消息窗口句柄备用}
FTimeID := timeSetEvent(1000 - FSpeed,1,@TimeCallBack,FWindowHandle,TIME_PERIODIC) //设置高精度计时器
end;
#15
同意楼上
#16
flyinwuhan(制怒·三思而后行) :
我的理解是windows会按照您设定的时间间隔准时的给你发送timer消息,但是如果您的程序处理其他的消息过长,期间windows给你的消息队列发送了数个timer消息,但是队列只保留一个,而忽略其他的。
结果造成两个timer事件的处理间隔不到一秒,也就是说timer是准时的,但是处理事件并不准时。
是这样吧?
我的理解是windows会按照您设定的时间间隔准时的给你发送timer消息,但是如果您的程序处理其他的消息过长,期间windows给你的消息队列发送了数个timer消息,但是队列只保留一个,而忽略其他的。
结果造成两个timer事件的处理间隔不到一秒,也就是说timer是准时的,但是处理事件并不准时。
是这样吧?
#17
好
#18
>>也就是说timer是准时的,但是处理事件并不准时。
YES
YES
#19
或者说,timer是不是准时的,跟你的程序没有关系,而是取决于系统,如果系统正处理繁忙的任务,就会导致timer不准时。。。。注意,是系统处理繁忙的任务造成timer不准时,而不是你的程序处理繁忙的任务造成timer不准时。。。
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的消息队列中的。
有些人瞎说。。。误人子弟。。。需要自己做实验才行。。。
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的消息队列中的。
有些人瞎说。。。误人子弟。。。需要自己做实验才行。。。
#20
我的 主程序都terminate了,但timer里的语句还要执行,导致错误。
timer事件里需要做的事比较多,耗时长,怎么办?
timer事件里需要做的事比较多,耗时长,怎么办?
#21
顶一下
#22
flyinwuhan(制怒·三思而后行)
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的
消息队列中的
千真万确,消息队列中在任何时候只保留一个wm_timer消息(当然,如果你定义了两个timer,那就保留两个wm_timer消息。不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了。
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的
消息队列中的
千真万确,消息队列中在任何时候只保留一个wm_timer消息(当然,如果你定义了两个timer,那就保留两个wm_timer消息。不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了。
#23
注:…………然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统…………
应该是:“timer消息处理是异步的了”
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统…………
应该是:“timer消息处理是异步的了”
#24
收藏了,有空再研究一下.
#25
niutuoshaozhe(牛拖少这) :
不管您是正常结束程序,还是调用application.terminate结束程序,结果都是在消息队列中放
个wm_quit消息,使fterminate属性为真,导致退出消息循环,结束程序。
但是消息循环只有在处理完timer事件之后,才能取到wm_quit消息,应该不会出现程序退出,timer还没有处理完的情况,除非你的程序有好几个消息循环。
不管您是正常结束程序,还是调用application.terminate结束程序,结果都是在消息队列中放
个wm_quit消息,使fterminate属性为真,导致退出消息循环,结束程序。
但是消息循环只有在处理完timer事件之后,才能取到wm_quit消息,应该不会出现程序退出,timer还没有处理完的情况,除非你的程序有好几个消息循环。
#26
>>千真万确,消息队列中在任何时候只保留一个wm_timer消息...不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了
你可以试验一下:
var
Form1: TForm1;
i : integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if i=0 then
sleep(3000);//休息3秒
if i>3 then
begin
timer1.enabled:=false;
exit;
end;
inc(i);
listbox1.items.add('xxx');//你的程序会停止3秒后突然连续触发3个timer事件!!
end;
你可以试验一下:
var
Form1: TForm1;
i : integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if i=0 then
sleep(3000);//休息3秒
if i>3 then
begin
timer1.enabled:=false;
exit;
end;
inc(i);
listbox1.items.add('xxx');//你的程序会停止3秒后突然连续触发3个timer事件!!
end;
#27
up 一下
#28
呵呵,我的实验有问题。没有深入分析啊~~~~~~~~~~~~
看来“消息队列中在任何时候只保留一个wm_timer消息”这句话是对的^_^
看来“消息队列中在任何时候只保留一个wm_timer消息”这句话是对的^_^
#29
Delphi中三种延时方法及其定时精度分析 选择自 listenwind 的 Blog
关键字 Delphi中三种延时方法及其定时精度分析
出处
在Delphi中,通常可以用以下三种方法来实现程序的延时,即TTtimer控件,Sleep函数,GetTickCount函数。但是其精度是各不相同的。
一、三种方法的简单介绍
1)TTtimer控件
TTtimer控件的实质是调用Windows API定时函数SetTimer和KillTimer来实现的,并简化了对WM_TIMER 消息的处理过程。通过设置OnTimer事件和Interval属性,我们可以很方便的产生一些简单的定时事件。
2)Sleep函数
Sleep函数用来使程序的执行延时给定的时间值。Sleep的调用形式为Sleep(milliseconds),暂停当前的进程milliseconds毫秒。Sleep的实现方法其实也是调用Windows API的Sleep函数。例如:
sleep(1000); //延迟1000毫秒
Sleep会引起程序停滞,如果你延迟的时间较长的话,你的程序将不能够响应延时期间的发生的其他消息,所以程序看起来好像暂时死机。
3)GetTickCount函数
在主程序中延时,为了达到延时和响应消息这两个目的,GetTickCount()构成的循环就是一种广为流传的方法。例如:
procedure Delay(MSecs: Longint);
//延时函数,MSecs单位为毫秒(千分之1秒)
var
FirstTickCount, Now: Longint;
begin
FirstTickCount := GetTickCount();
repeat
Application.ProcessMessages;
Now := GetTickCount();
until (Now - FirstTickCount >= MSecs) or (Now < FirstTickCount);
end;
二、高精度的微妙级性能计数器(high-resolution performance counter)介绍
为了比较以上方法的精度,首先需要找到一个参考的定时器。在这里,我提供了两个参考的定时器。一是用单片机每隔1.024ms产生一个实时中断RTI,作为计数器;二是选用了一个高精度的微妙级性能计数器(参见: http://msdn.microsoft.com/msdnmag/issues/04/03/HighResolutionTimer/default.aspx ,或者 http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
)
1)计数器的Delphi源代码
{
A high-precision counter/timer. Retrieves time differences
downto microsec.
Quick Reference:
THPCounter inherits from TComponent.
Key-Methods:
Start: Starts the counter. Place this call just before the
code you want to measure.
Read: Reads the counter as a string. Place this call just
after the code you want to measure.
ReadInt: Reads the counter as an Int64. Place this call just
after the code you want to measure.
--------------------------------------------------------------------------------
}
unit HPCounter;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls;
type
TInt64 = TLargeInteger;
THPCounter = class(TComponent)
private
Frequency: TLargeInteger;
lpPerformanceCount1: TLargeInteger;
lpPerformanceCount2: TLargeInteger;
fAbout: string;
procedure SetAbout(Value: string);
{ Private declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Start;
function Read: string;
function ReadInt: TLargeInteger;
{ Private declarations }
published
property About: string read fAbout write SetAbout;
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MAs Prod.', [THPCounter]);
end;
constructor THPCounter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fAbout:= 'Version 1.1, 2000® Mats Asplund, EMail: masprod@telia.com, Site: http://go.to/masdp';
end;
destructor THPCounter.Destroy;
begin
inherited Destroy;
end;
function THPCounter.Read: string;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=IntToStr(Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency));
end;
function THPCounter.ReadInt: TLargeInteger;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency);
end;
procedure THPCounter.SetAbout(Value: string);
begin
Exit;
end;
procedure THPCounter.Start;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount1)^));
end;
end.
2)使用方法:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
HPCounter, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:= '';
Application.ProcessMessages;
with THPCounter.Create(Self) do
begin
Start;
// Place code to measure here
Sleep(1000);
// Place code to measure here
Edit1.Text:=Read;
Free;
end;
end;
end.
二、三种方法的精度比较
为了比较,采用以上3种方法,分别设置延时时间为1ms、2ms、5ms、10ms、20ms、50ms、100ms、200ms、500ms、1000ms,循环次数为5次,得到实际的延时时间。
1)TTtimer控件
实际延时时间(ms)
1ms: 8.012 21.551 6.875 21.647 9.809
2ms: 9.957 20.675 14.671 11.903 20.551
5ms: 9.952 20.605 9.924 20.705 12.682
10ms:14.852 9.96 21.547 9.82 20.634
20ms:27.512 34.291 26.427 31.244 30.398
50ms:61.196 61.307 64.027 62.048 63.059
100ms:102.495 108.408 112.318 110.322 102.531
200ms:193.955 202.135 207.016 205.082 202.194
500ms:496.659 500.534 503.398 495.551 500.394
1000ms:999.699 1003.576 993.698 1004.443 995.625
2)Sleep函数
1ms: 1.895 1.895 1.896 1.897 1.898
2ms: 2.868 2.874 2.852 2.872 2.869
5ms: 5.8 5.797 5.79 5.79 5.791
10ms:10.675 10.683 10.611 10.669 10.67
20ms:20.404 20.434 20.447 20.477 20.368
50ms:50.67 50.691 50.69 50.682 50.671
100ms:100.515 100.469 100.484 100.481 100.484
200ms:200.101 200.126 199.892 200.066 200.108
500ms:499.961 499.961 499.958 499.961 499.96
1000ms:1000.034 1000.04 1000.03 1000.018 1000.029
3)GetTickCount函数
1ms: 15.54 15.596 15.527 15.566 15.838
2ms: 15.561 15.563 15.603 15.477 15.571
5ms: 15.519 15.549 15.569 15.666 15.394
10ms:15.558 15.561 15.522 15.568 15.518
20ms:31.186 31.137 31.17 31.17 31.19
50ms:62.445 62.4 63.893 60.88 62.404
100ms:109.276 109.298 109.273 109.28 109.28
200ms:203.027 203.084 203.021 203.027 203.046
500ms:499.959 499.961 499.963 499.967 499.965
1000ms:1000.023 1000.022 1000.026 1000.029 1000.021
可见,相对而言,Sleep的精度最高,尤其是在10ms以内的延时,只有sleep函数才能够做到。TTimer控件的定时精度最差,而且稳定性不好,波动很大。GetTickCount函数所能实现的最短延时为15ms左右,稳定性相对TTimer要好一些。
关键字 Delphi中三种延时方法及其定时精度分析
出处
在Delphi中,通常可以用以下三种方法来实现程序的延时,即TTtimer控件,Sleep函数,GetTickCount函数。但是其精度是各不相同的。
一、三种方法的简单介绍
1)TTtimer控件
TTtimer控件的实质是调用Windows API定时函数SetTimer和KillTimer来实现的,并简化了对WM_TIMER 消息的处理过程。通过设置OnTimer事件和Interval属性,我们可以很方便的产生一些简单的定时事件。
2)Sleep函数
Sleep函数用来使程序的执行延时给定的时间值。Sleep的调用形式为Sleep(milliseconds),暂停当前的进程milliseconds毫秒。Sleep的实现方法其实也是调用Windows API的Sleep函数。例如:
sleep(1000); //延迟1000毫秒
Sleep会引起程序停滞,如果你延迟的时间较长的话,你的程序将不能够响应延时期间的发生的其他消息,所以程序看起来好像暂时死机。
3)GetTickCount函数
在主程序中延时,为了达到延时和响应消息这两个目的,GetTickCount()构成的循环就是一种广为流传的方法。例如:
procedure Delay(MSecs: Longint);
//延时函数,MSecs单位为毫秒(千分之1秒)
var
FirstTickCount, Now: Longint;
begin
FirstTickCount := GetTickCount();
repeat
Application.ProcessMessages;
Now := GetTickCount();
until (Now - FirstTickCount >= MSecs) or (Now < FirstTickCount);
end;
二、高精度的微妙级性能计数器(high-resolution performance counter)介绍
为了比较以上方法的精度,首先需要找到一个参考的定时器。在这里,我提供了两个参考的定时器。一是用单片机每隔1.024ms产生一个实时中断RTI,作为计数器;二是选用了一个高精度的微妙级性能计数器(参见: http://msdn.microsoft.com/msdnmag/issues/04/03/HighResolutionTimer/default.aspx ,或者 http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
)
1)计数器的Delphi源代码
{
A high-precision counter/timer. Retrieves time differences
downto microsec.
Quick Reference:
THPCounter inherits from TComponent.
Key-Methods:
Start: Starts the counter. Place this call just before the
code you want to measure.
Read: Reads the counter as a string. Place this call just
after the code you want to measure.
ReadInt: Reads the counter as an Int64. Place this call just
after the code you want to measure.
--------------------------------------------------------------------------------
}
unit HPCounter;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls;
type
TInt64 = TLargeInteger;
THPCounter = class(TComponent)
private
Frequency: TLargeInteger;
lpPerformanceCount1: TLargeInteger;
lpPerformanceCount2: TLargeInteger;
fAbout: string;
procedure SetAbout(Value: string);
{ Private declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Start;
function Read: string;
function ReadInt: TLargeInteger;
{ Private declarations }
published
property About: string read fAbout write SetAbout;
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MAs Prod.', [THPCounter]);
end;
constructor THPCounter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fAbout:= 'Version 1.1, 2000® Mats Asplund, EMail: masprod@telia.com, Site: http://go.to/masdp';
end;
destructor THPCounter.Destroy;
begin
inherited Destroy;
end;
function THPCounter.Read: string;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=IntToStr(Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency));
end;
function THPCounter.ReadInt: TLargeInteger;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency);
end;
procedure THPCounter.SetAbout(Value: string);
begin
Exit;
end;
procedure THPCounter.Start;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount1)^));
end;
end.
2)使用方法:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
HPCounter, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:= '';
Application.ProcessMessages;
with THPCounter.Create(Self) do
begin
Start;
// Place code to measure here
Sleep(1000);
// Place code to measure here
Edit1.Text:=Read;
Free;
end;
end;
end.
二、三种方法的精度比较
为了比较,采用以上3种方法,分别设置延时时间为1ms、2ms、5ms、10ms、20ms、50ms、100ms、200ms、500ms、1000ms,循环次数为5次,得到实际的延时时间。
1)TTtimer控件
实际延时时间(ms)
1ms: 8.012 21.551 6.875 21.647 9.809
2ms: 9.957 20.675 14.671 11.903 20.551
5ms: 9.952 20.605 9.924 20.705 12.682
10ms:14.852 9.96 21.547 9.82 20.634
20ms:27.512 34.291 26.427 31.244 30.398
50ms:61.196 61.307 64.027 62.048 63.059
100ms:102.495 108.408 112.318 110.322 102.531
200ms:193.955 202.135 207.016 205.082 202.194
500ms:496.659 500.534 503.398 495.551 500.394
1000ms:999.699 1003.576 993.698 1004.443 995.625
2)Sleep函数
1ms: 1.895 1.895 1.896 1.897 1.898
2ms: 2.868 2.874 2.852 2.872 2.869
5ms: 5.8 5.797 5.79 5.79 5.791
10ms:10.675 10.683 10.611 10.669 10.67
20ms:20.404 20.434 20.447 20.477 20.368
50ms:50.67 50.691 50.69 50.682 50.671
100ms:100.515 100.469 100.484 100.481 100.484
200ms:200.101 200.126 199.892 200.066 200.108
500ms:499.961 499.961 499.958 499.961 499.96
1000ms:1000.034 1000.04 1000.03 1000.018 1000.029
3)GetTickCount函数
1ms: 15.54 15.596 15.527 15.566 15.838
2ms: 15.561 15.563 15.603 15.477 15.571
5ms: 15.519 15.549 15.569 15.666 15.394
10ms:15.558 15.561 15.522 15.568 15.518
20ms:31.186 31.137 31.17 31.17 31.19
50ms:62.445 62.4 63.893 60.88 62.404
100ms:109.276 109.298 109.273 109.28 109.28
200ms:203.027 203.084 203.021 203.027 203.046
500ms:499.959 499.961 499.963 499.967 499.965
1000ms:1000.023 1000.022 1000.026 1000.029 1000.021
可见,相对而言,Sleep的精度最高,尤其是在10ms以内的延时,只有sleep函数才能够做到。TTimer控件的定时精度最差,而且稳定性不好,波动很大。GetTickCount函数所能实现的最短延时为15ms左右,稳定性相对TTimer要好一些。
#30
帮顶一下
#31
up一下
#32
如果怕死机,那建议用多线程处理
#33
学习一下.
#34
多线程
#1
你中断了1250毫秒之后,得到的当然是间隔1250毫秒之后的时间啊,郁闷
#2
但是你的TIMER是1秒一次,所以造成了这个延时显示
#3
我的意思是如果你想很精确的每一秒钟执行一个事件是不可能的,timer事件并不是异步执行的
#4
關于timer , WM_TIMER的,在 delphi 深度歷險面一書中,說得很清楚
#5
>>如果你的timer事件的处理需要一定的时间,那么在段时间内,其他新加入队列的消息
就得不到及时的处理,程序看起来象死机一样,此时该怎办??调用processmessages??
调用processmessages是一个办法
但是如果处理时间>3秒,我想使用TImer事件就不太好了 还是提交给另一个线程吧
就得不到及时的处理,程序看起来象死机一样,此时该怎办??调用processmessages??
调用processmessages是一个办法
但是如果处理时间>3秒,我想使用TImer事件就不太好了 还是提交给另一个线程吧
#6
因为sleep(1250);的中断等级高于Timer,所以先中断1.25秒再计时,出现这样的问题是当然的。
#7
你可以参考大富翁论坛
关于:"时钟控件定时产生线程操作数据库,shi线程资源释放的问题"
该问题的URL是: http://www.delphibbs.com/delphibbs/dispq.asp?LID=673905
可能会有点启发。
关于:"时钟控件定时产生线程操作数据库,shi线程资源释放的问题"
该问题的URL是: http://www.delphibbs.com/delphibbs/dispq.asp?LID=673905
可能会有点启发。
#8
〉〉我的意思是如果你想很精确的每一秒钟执行一个事件是不可能的,timer事件并不是异步执行的
为啥不sleep(5250);?这样更容易看出问题
为啥不sleep(5250);?这样更容易看出问题
#9
timer在消息队列中的等级是最低的,即便不用sleep,使用其他方法使该timer事件执行时间超过1秒,或者程序中处理其他的消息超过一秒,那么结果也会是1秒钟的timer不会真正的一秒触发一次。
是不是这样??
是不是这样??
#10
还有一个问题,timer的最低分辨率是大概10毫秒,那么如果你的timer间隔是1毫秒的话,也会10毫秒触发一次,有没有其他的办法使计时器能够更加精确??
#11
最新发现,timer事件的触发间隔是完全无规律的,甚至两个timer事件的时间间隔比你定义的还要短,有
程序为证:
定义两个全局变量:
iInterval: integer;
dtBegin: TDateTime;
timer事件如下:
procedure TForm1.Timer1Timer(Sender: TObject);
var
boolFlag: boolean;
dtCur: TDateTime;
h,m,s,ms: Word;
begin
boolFlag := true;
while boolFlag do
begin
iInterval := iInterVal + 1;
dtCur := Now();
DecodeTime((dtCur - dtBegin),h,m,s,ms);
Memo1.Lines.Add(Inttostr(s*1000 + ms));
dtBegin := dtCur;
if iInterval mod 2 = 0 then
Sleep(1600)
else
Sleep(200);
boolFlag := false;
end;
end;
结果的规律如下:
1002
1602
401
1602
401
1602
400
1603
400
1603
400
1603
400
1602
401
1602
401
1602
401
1602
401
1602
这表明我定义1秒触发一次的timer,由于我的timer事件执行时间过长,导致两个timer事件之间间隔
小于1秒
程序为证:
定义两个全局变量:
iInterval: integer;
dtBegin: TDateTime;
timer事件如下:
procedure TForm1.Timer1Timer(Sender: TObject);
var
boolFlag: boolean;
dtCur: TDateTime;
h,m,s,ms: Word;
begin
boolFlag := true;
while boolFlag do
begin
iInterval := iInterVal + 1;
dtCur := Now();
DecodeTime((dtCur - dtBegin),h,m,s,ms);
Memo1.Lines.Add(Inttostr(s*1000 + ms));
dtBegin := dtCur;
if iInterval mod 2 = 0 then
Sleep(1600)
else
Sleep(200);
boolFlag := false;
end;
end;
结果的规律如下:
1002
1602
401
1602
401
1602
400
1603
400
1603
400
1603
400
1602
401
1602
401
1602
401
1602
401
1602
这表明我定义1秒触发一次的timer,由于我的timer事件执行时间过长,导致两个timer事件之间间隔
小于1秒
#12
>>timer在消息队列中的等级是最低的,即便不用sleep,使用其他方法使该timer事件执行时间超过1秒,或者程序中处理其他的消息超过一秒,那么结果也会是1秒钟的timer不会真正的一秒触发一次。
是不是这样??
---肯定的啊
>>有没有其他的办法使计时器能够更加精确??
---多媒体计时器
>>timer事件的触发间隔是完全无规律的。。。甚至两个timer事件的时间间隔比你定义的还要短
---错!
procedure TForm1.Timer1Timer(Sender: TObject);
...
begin
boolFlag := true;
while boolFlag do
begin
...
if iInterval mod 2 = 0 then
Sleep(1600)//你sleep了,所以你得程序不会处理(响应)任何消息了~~~
//但是系统还是按时给你发送了一个timer消息,可是你在睡觉,不响应它,
//当你醒过来处理这个消息的时候,时间已经过去了
//1600-1000=600ms,所以这次的间隔是1600ms;
else
Sleep(200);//你又sleep了,但是系统按时给你发送了一个timer消息,这次的是在你醒来200ms后 //后接到的,所以sleep的200ms+200ms=400ms....一切都是很有规律的啊!!!~
boolFlag := false;
end;
end;
是不是这样??
---肯定的啊
>>有没有其他的办法使计时器能够更加精确??
---多媒体计时器
>>timer事件的触发间隔是完全无规律的。。。甚至两个timer事件的时间间隔比你定义的还要短
---错!
procedure TForm1.Timer1Timer(Sender: TObject);
...
begin
boolFlag := true;
while boolFlag do
begin
...
if iInterval mod 2 = 0 then
Sleep(1600)//你sleep了,所以你得程序不会处理(响应)任何消息了~~~
//但是系统还是按时给你发送了一个timer消息,可是你在睡觉,不响应它,
//当你醒过来处理这个消息的时候,时间已经过去了
//1600-1000=600ms,所以这次的间隔是1600ms;
else
Sleep(200);//你又sleep了,但是系统按时给你发送了一个timer消息,这次的是在你醒来200ms后 //后接到的,所以sleep的200ms+200ms=400ms....一切都是很有规律的啊!!!~
boolFlag := false;
end;
end;
#13
Timer是不准时的!
想可靠就用多媒体定时器
我在FAQ中提过的,http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
http://lysoft.7u7.net
想可靠就用多媒体定时器
我在FAQ中提过的,http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
http://lysoft.7u7.net
#14
TTimer 精确度在 50 - 49 ms 之间 Windows2K 比 Win98 稍准一点
你要实现小于50ms的计时用TTimer是不可能实现滴 楼上多提到的多嫖体定时器应用
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD); stdcall; //计时器回调函数
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD);
begin
PostMessage(dwUser,CM_MARQUEE,dwUser,uTimerID);
end;
procedure TSupperLED.SetHPTimer;
var
pTP: PTimeCaps;
begin
if FTimeID <> 0 then DeleteHPTimer;
GetMem(pTP,SizeOf(TTimeCaps));
try
timeGetDevCaps(pTP,SizeOf(TTimeCaps)); //取当前系统最高计时精度能力
timeBeginPeriod(pTP^.wPeriodMin); //设置计时精度为最佳
finally
FreeMem(pTP,SizeOf(TTimeCaps));
end;
{传入接收消息窗口句柄备用}
FTimeID := timeSetEvent(1000 - FSpeed,1,@TimeCallBack,FWindowHandle,TIME_PERIODIC) //设置高精度计时器
end;
你要实现小于50ms的计时用TTimer是不可能实现滴 楼上多提到的多嫖体定时器应用
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD); stdcall; //计时器回调函数
procedure TimeCallBack(uTimerID, uMessage: UINT;dwUser, dw1, dw2: DWORD);
begin
PostMessage(dwUser,CM_MARQUEE,dwUser,uTimerID);
end;
procedure TSupperLED.SetHPTimer;
var
pTP: PTimeCaps;
begin
if FTimeID <> 0 then DeleteHPTimer;
GetMem(pTP,SizeOf(TTimeCaps));
try
timeGetDevCaps(pTP,SizeOf(TTimeCaps)); //取当前系统最高计时精度能力
timeBeginPeriod(pTP^.wPeriodMin); //设置计时精度为最佳
finally
FreeMem(pTP,SizeOf(TTimeCaps));
end;
{传入接收消息窗口句柄备用}
FTimeID := timeSetEvent(1000 - FSpeed,1,@TimeCallBack,FWindowHandle,TIME_PERIODIC) //设置高精度计时器
end;
#15
同意楼上
#16
flyinwuhan(制怒·三思而后行) :
我的理解是windows会按照您设定的时间间隔准时的给你发送timer消息,但是如果您的程序处理其他的消息过长,期间windows给你的消息队列发送了数个timer消息,但是队列只保留一个,而忽略其他的。
结果造成两个timer事件的处理间隔不到一秒,也就是说timer是准时的,但是处理事件并不准时。
是这样吧?
我的理解是windows会按照您设定的时间间隔准时的给你发送timer消息,但是如果您的程序处理其他的消息过长,期间windows给你的消息队列发送了数个timer消息,但是队列只保留一个,而忽略其他的。
结果造成两个timer事件的处理间隔不到一秒,也就是说timer是准时的,但是处理事件并不准时。
是这样吧?
#17
好
#18
>>也就是说timer是准时的,但是处理事件并不准时。
YES
YES
#19
或者说,timer是不是准时的,跟你的程序没有关系,而是取决于系统,如果系统正处理繁忙的任务,就会导致timer不准时。。。。注意,是系统处理繁忙的任务造成timer不准时,而不是你的程序处理繁忙的任务造成timer不准时。。。
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的消息队列中的。
有些人瞎说。。。误人子弟。。。需要自己做实验才行。。。
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的消息队列中的。
有些人瞎说。。。误人子弟。。。需要自己做实验才行。。。
#20
我的 主程序都terminate了,但timer里的语句还要执行,导致错误。
timer事件里需要做的事比较多,耗时长,怎么办?
timer事件里需要做的事比较多,耗时长,怎么办?
#21
顶一下
#22
flyinwuhan(制怒·三思而后行)
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的
消息队列中的
千真万确,消息队列中在任何时候只保留一个wm_timer消息(当然,如果你定义了两个timer,那就保留两个wm_timer消息。不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了。
>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次
这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的
消息队列中的
千真万确,消息队列中在任何时候只保留一个wm_timer消息(当然,如果你定义了两个timer,那就保留两个wm_timer消息。不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了。
#23
注:…………然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统…………
应该是:“timer消息处理是异步的了”
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统…………
应该是:“timer消息处理是异步的了”
#24
收藏了,有空再研究一下.
#25
niutuoshaozhe(牛拖少这) :
不管您是正常结束程序,还是调用application.terminate结束程序,结果都是在消息队列中放
个wm_quit消息,使fterminate属性为真,导致退出消息循环,结束程序。
但是消息循环只有在处理完timer事件之后,才能取到wm_quit消息,应该不会出现程序退出,timer还没有处理完的情况,除非你的程序有好几个消息循环。
不管您是正常结束程序,还是调用application.terminate结束程序,结果都是在消息队列中放
个wm_quit消息,使fterminate属性为真,导致退出消息循环,结束程序。
但是消息循环只有在处理完timer事件之后,才能取到wm_quit消息,应该不会出现程序退出,timer还没有处理完的情况,除非你的程序有好几个消息循环。
#26
>>千真万确,消息队列中在任何时候只保留一个wm_timer消息...不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了
你可以试验一下:
var
Form1: TForm1;
i : integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if i=0 then
sleep(3000);//休息3秒
if i>3 then
begin
timer1.enabled:=false;
exit;
end;
inc(i);
listbox1.items.add('xxx');//你的程序会停止3秒后突然连续触发3个timer事件!!
end;
你可以试验一下:
var
Form1: TForm1;
i : integer;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if i=0 then
sleep(3000);//休息3秒
if i>3 then
begin
timer1.enabled:=false;
exit;
end;
inc(i);
listbox1.items.add('xxx');//你的程序会停止3秒后突然连续触发3个timer事件!!
end;
#27
up 一下
#28
呵呵,我的实验有问题。没有深入分析啊~~~~~~~~~~~~
看来“消息队列中在任何时候只保留一个wm_timer消息”这句话是对的^_^
看来“消息队列中在任何时候只保留一个wm_timer消息”这句话是对的^_^
#29
Delphi中三种延时方法及其定时精度分析 选择自 listenwind 的 Blog
关键字 Delphi中三种延时方法及其定时精度分析
出处
在Delphi中,通常可以用以下三种方法来实现程序的延时,即TTtimer控件,Sleep函数,GetTickCount函数。但是其精度是各不相同的。
一、三种方法的简单介绍
1)TTtimer控件
TTtimer控件的实质是调用Windows API定时函数SetTimer和KillTimer来实现的,并简化了对WM_TIMER 消息的处理过程。通过设置OnTimer事件和Interval属性,我们可以很方便的产生一些简单的定时事件。
2)Sleep函数
Sleep函数用来使程序的执行延时给定的时间值。Sleep的调用形式为Sleep(milliseconds),暂停当前的进程milliseconds毫秒。Sleep的实现方法其实也是调用Windows API的Sleep函数。例如:
sleep(1000); //延迟1000毫秒
Sleep会引起程序停滞,如果你延迟的时间较长的话,你的程序将不能够响应延时期间的发生的其他消息,所以程序看起来好像暂时死机。
3)GetTickCount函数
在主程序中延时,为了达到延时和响应消息这两个目的,GetTickCount()构成的循环就是一种广为流传的方法。例如:
procedure Delay(MSecs: Longint);
//延时函数,MSecs单位为毫秒(千分之1秒)
var
FirstTickCount, Now: Longint;
begin
FirstTickCount := GetTickCount();
repeat
Application.ProcessMessages;
Now := GetTickCount();
until (Now - FirstTickCount >= MSecs) or (Now < FirstTickCount);
end;
二、高精度的微妙级性能计数器(high-resolution performance counter)介绍
为了比较以上方法的精度,首先需要找到一个参考的定时器。在这里,我提供了两个参考的定时器。一是用单片机每隔1.024ms产生一个实时中断RTI,作为计数器;二是选用了一个高精度的微妙级性能计数器(参见: http://msdn.microsoft.com/msdnmag/issues/04/03/HighResolutionTimer/default.aspx ,或者 http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
)
1)计数器的Delphi源代码
{
A high-precision counter/timer. Retrieves time differences
downto microsec.
Quick Reference:
THPCounter inherits from TComponent.
Key-Methods:
Start: Starts the counter. Place this call just before the
code you want to measure.
Read: Reads the counter as a string. Place this call just
after the code you want to measure.
ReadInt: Reads the counter as an Int64. Place this call just
after the code you want to measure.
--------------------------------------------------------------------------------
}
unit HPCounter;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls;
type
TInt64 = TLargeInteger;
THPCounter = class(TComponent)
private
Frequency: TLargeInteger;
lpPerformanceCount1: TLargeInteger;
lpPerformanceCount2: TLargeInteger;
fAbout: string;
procedure SetAbout(Value: string);
{ Private declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Start;
function Read: string;
function ReadInt: TLargeInteger;
{ Private declarations }
published
property About: string read fAbout write SetAbout;
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MAs Prod.', [THPCounter]);
end;
constructor THPCounter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fAbout:= 'Version 1.1, 2000® Mats Asplund, EMail: masprod@telia.com, Site: http://go.to/masdp';
end;
destructor THPCounter.Destroy;
begin
inherited Destroy;
end;
function THPCounter.Read: string;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=IntToStr(Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency));
end;
function THPCounter.ReadInt: TLargeInteger;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency);
end;
procedure THPCounter.SetAbout(Value: string);
begin
Exit;
end;
procedure THPCounter.Start;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount1)^));
end;
end.
2)使用方法:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
HPCounter, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:= '';
Application.ProcessMessages;
with THPCounter.Create(Self) do
begin
Start;
// Place code to measure here
Sleep(1000);
// Place code to measure here
Edit1.Text:=Read;
Free;
end;
end;
end.
二、三种方法的精度比较
为了比较,采用以上3种方法,分别设置延时时间为1ms、2ms、5ms、10ms、20ms、50ms、100ms、200ms、500ms、1000ms,循环次数为5次,得到实际的延时时间。
1)TTtimer控件
实际延时时间(ms)
1ms: 8.012 21.551 6.875 21.647 9.809
2ms: 9.957 20.675 14.671 11.903 20.551
5ms: 9.952 20.605 9.924 20.705 12.682
10ms:14.852 9.96 21.547 9.82 20.634
20ms:27.512 34.291 26.427 31.244 30.398
50ms:61.196 61.307 64.027 62.048 63.059
100ms:102.495 108.408 112.318 110.322 102.531
200ms:193.955 202.135 207.016 205.082 202.194
500ms:496.659 500.534 503.398 495.551 500.394
1000ms:999.699 1003.576 993.698 1004.443 995.625
2)Sleep函数
1ms: 1.895 1.895 1.896 1.897 1.898
2ms: 2.868 2.874 2.852 2.872 2.869
5ms: 5.8 5.797 5.79 5.79 5.791
10ms:10.675 10.683 10.611 10.669 10.67
20ms:20.404 20.434 20.447 20.477 20.368
50ms:50.67 50.691 50.69 50.682 50.671
100ms:100.515 100.469 100.484 100.481 100.484
200ms:200.101 200.126 199.892 200.066 200.108
500ms:499.961 499.961 499.958 499.961 499.96
1000ms:1000.034 1000.04 1000.03 1000.018 1000.029
3)GetTickCount函数
1ms: 15.54 15.596 15.527 15.566 15.838
2ms: 15.561 15.563 15.603 15.477 15.571
5ms: 15.519 15.549 15.569 15.666 15.394
10ms:15.558 15.561 15.522 15.568 15.518
20ms:31.186 31.137 31.17 31.17 31.19
50ms:62.445 62.4 63.893 60.88 62.404
100ms:109.276 109.298 109.273 109.28 109.28
200ms:203.027 203.084 203.021 203.027 203.046
500ms:499.959 499.961 499.963 499.967 499.965
1000ms:1000.023 1000.022 1000.026 1000.029 1000.021
可见,相对而言,Sleep的精度最高,尤其是在10ms以内的延时,只有sleep函数才能够做到。TTimer控件的定时精度最差,而且稳定性不好,波动很大。GetTickCount函数所能实现的最短延时为15ms左右,稳定性相对TTimer要好一些。
关键字 Delphi中三种延时方法及其定时精度分析
出处
在Delphi中,通常可以用以下三种方法来实现程序的延时,即TTtimer控件,Sleep函数,GetTickCount函数。但是其精度是各不相同的。
一、三种方法的简单介绍
1)TTtimer控件
TTtimer控件的实质是调用Windows API定时函数SetTimer和KillTimer来实现的,并简化了对WM_TIMER 消息的处理过程。通过设置OnTimer事件和Interval属性,我们可以很方便的产生一些简单的定时事件。
2)Sleep函数
Sleep函数用来使程序的执行延时给定的时间值。Sleep的调用形式为Sleep(milliseconds),暂停当前的进程milliseconds毫秒。Sleep的实现方法其实也是调用Windows API的Sleep函数。例如:
sleep(1000); //延迟1000毫秒
Sleep会引起程序停滞,如果你延迟的时间较长的话,你的程序将不能够响应延时期间的发生的其他消息,所以程序看起来好像暂时死机。
3)GetTickCount函数
在主程序中延时,为了达到延时和响应消息这两个目的,GetTickCount()构成的循环就是一种广为流传的方法。例如:
procedure Delay(MSecs: Longint);
//延时函数,MSecs单位为毫秒(千分之1秒)
var
FirstTickCount, Now: Longint;
begin
FirstTickCount := GetTickCount();
repeat
Application.ProcessMessages;
Now := GetTickCount();
until (Now - FirstTickCount >= MSecs) or (Now < FirstTickCount);
end;
二、高精度的微妙级性能计数器(high-resolution performance counter)介绍
为了比较以上方法的精度,首先需要找到一个参考的定时器。在这里,我提供了两个参考的定时器。一是用单片机每隔1.024ms产生一个实时中断RTI,作为计数器;二是选用了一个高精度的微妙级性能计数器(参见: http://msdn.microsoft.com/msdnmag/issues/04/03/HighResolutionTimer/default.aspx ,或者 http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=200249
)
1)计数器的Delphi源代码
{
A high-precision counter/timer. Retrieves time differences
downto microsec.
Quick Reference:
THPCounter inherits from TComponent.
Key-Methods:
Start: Starts the counter. Place this call just before the
code you want to measure.
Read: Reads the counter as a string. Place this call just
after the code you want to measure.
ReadInt: Reads the counter as an Int64. Place this call just
after the code you want to measure.
--------------------------------------------------------------------------------
}
unit HPCounter;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ExtCtrls;
type
TInt64 = TLargeInteger;
THPCounter = class(TComponent)
private
Frequency: TLargeInteger;
lpPerformanceCount1: TLargeInteger;
lpPerformanceCount2: TLargeInteger;
fAbout: string;
procedure SetAbout(Value: string);
{ Private declarations }
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Start;
function Read: string;
function ReadInt: TLargeInteger;
{ Private declarations }
published
property About: string read fAbout write SetAbout;
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MAs Prod.', [THPCounter]);
end;
constructor THPCounter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fAbout:= 'Version 1.1, 2000® Mats Asplund, EMail: masprod@telia.com, Site: http://go.to/masdp';
end;
destructor THPCounter.Destroy;
begin
inherited Destroy;
end;
function THPCounter.Read: string;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=IntToStr(Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency));
end;
function THPCounter.ReadInt: TLargeInteger;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount2)^));
QueryPerformanceFrequency(TInt64((@Frequency)^));
Result:=Round(1000000 * (lpPerformanceCount2 -
lpPerformanceCount1) / Frequency);
end;
procedure THPCounter.SetAbout(Value: string);
begin
Exit;
end;
procedure THPCounter.Start;
begin
QueryPerformanceCounter(TInt64((@lpPerformanceCount1)^));
end;
end.
2)使用方法:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
HPCounter, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:= '';
Application.ProcessMessages;
with THPCounter.Create(Self) do
begin
Start;
// Place code to measure here
Sleep(1000);
// Place code to measure here
Edit1.Text:=Read;
Free;
end;
end;
end.
二、三种方法的精度比较
为了比较,采用以上3种方法,分别设置延时时间为1ms、2ms、5ms、10ms、20ms、50ms、100ms、200ms、500ms、1000ms,循环次数为5次,得到实际的延时时间。
1)TTtimer控件
实际延时时间(ms)
1ms: 8.012 21.551 6.875 21.647 9.809
2ms: 9.957 20.675 14.671 11.903 20.551
5ms: 9.952 20.605 9.924 20.705 12.682
10ms:14.852 9.96 21.547 9.82 20.634
20ms:27.512 34.291 26.427 31.244 30.398
50ms:61.196 61.307 64.027 62.048 63.059
100ms:102.495 108.408 112.318 110.322 102.531
200ms:193.955 202.135 207.016 205.082 202.194
500ms:496.659 500.534 503.398 495.551 500.394
1000ms:999.699 1003.576 993.698 1004.443 995.625
2)Sleep函数
1ms: 1.895 1.895 1.896 1.897 1.898
2ms: 2.868 2.874 2.852 2.872 2.869
5ms: 5.8 5.797 5.79 5.79 5.791
10ms:10.675 10.683 10.611 10.669 10.67
20ms:20.404 20.434 20.447 20.477 20.368
50ms:50.67 50.691 50.69 50.682 50.671
100ms:100.515 100.469 100.484 100.481 100.484
200ms:200.101 200.126 199.892 200.066 200.108
500ms:499.961 499.961 499.958 499.961 499.96
1000ms:1000.034 1000.04 1000.03 1000.018 1000.029
3)GetTickCount函数
1ms: 15.54 15.596 15.527 15.566 15.838
2ms: 15.561 15.563 15.603 15.477 15.571
5ms: 15.519 15.549 15.569 15.666 15.394
10ms:15.558 15.561 15.522 15.568 15.518
20ms:31.186 31.137 31.17 31.17 31.19
50ms:62.445 62.4 63.893 60.88 62.404
100ms:109.276 109.298 109.273 109.28 109.28
200ms:203.027 203.084 203.021 203.027 203.046
500ms:499.959 499.961 499.963 499.967 499.965
1000ms:1000.023 1000.022 1000.026 1000.029 1000.021
可见,相对而言,Sleep的精度最高,尤其是在10ms以内的延时,只有sleep函数才能够做到。TTimer控件的定时精度最差,而且稳定性不好,波动很大。GetTickCount函数所能实现的最短延时为15ms左右,稳定性相对TTimer要好一些。
#30
帮顶一下
#31
up一下
#32
如果怕死机,那建议用多线程处理
#33
学习一下.
#34
多线程