Timer并不简单,请大家进来讨论一下

时间:2022-07-14 21:30:53
昨天看到一本书中介绍了关于WM_TIMER消息的工作原理,结合我自己的理解阐述如下:
具体如何根据硬件的时钟滴答来发送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事件就不太好了  还是提交给另一个线程吧

#6


因为sleep(1250);的中断等级高于Timer,所以先中断1.25秒再计时,出现这样的问题是当然的。

#7


你可以参考大富翁论坛
关于:"时钟控件定时产生线程操作数据库,shi线程资源释放的问题"
该问题的URL是: http://www.delphibbs.com/delphibbs/dispq.asp?LID=673905
可能会有点启发。

#8


〉〉我的意思是如果你想很精确的每一秒钟执行一个事件是不可能的,timer事件并不是异步执行的
为啥不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秒

#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;

#13


Timer是不准时的!
想可靠就用多媒体定时器
我在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;

#15


同意楼上

#16


flyinwuhan(制怒·三思而后行)  :
我的理解是windows会按照您设定的时间间隔准时的给你发送timer消息,但是如果您的程序处理其他的消息过长,期间windows给你的消息队列发送了数个timer消息,但是队列只保留一个,而忽略其他的。
结果造成两个timer事件的处理间隔不到一秒,也就是说timer是准时的,但是处理事件并不准时。
是这样吧?

#17


#18


>>也就是说timer是准时的,但是处理事件并不准时。
YES

#19


或者说,timer是不是准时的,跟你的程序没有关系,而是取决于系统,如果系统正处理繁忙的任务,就会导致timer不准时。。。。注意,是系统处理繁忙的任务造成timer不准时,而不是你的程序处理繁忙的任务造成timer不准时。。。

>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次

这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的消息队列中的。

有些人瞎说。。。误人子弟。。。需要自己做实验才行。。。

#20


我的 主程序都terminate了,但timer里的语句还要执行,导致错误。
timer事件里需要做的事比较多,耗时长,怎么办?

#21


顶一下

#22


flyinwuhan(制怒·三思而后行) 

  >>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,  
    例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
    只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次

    这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的
    消息队列中的

  千真万确,消息队列中在任何时候只保留一个wm_timer消息(当然,如果你定义了两个timer,那就保留两个wm_timer消息。不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了。

#23


注:…………然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统…………

应该是:“timer消息处理是异步的了”

#24


收藏了,有空再研究一下.

#25


niutuoshaozhe(牛拖少这) :
不管您是正常结束程序,还是调用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;

#27


up 一下

#28


呵呵,我的实验有问题。没有深入分析啊~~~~~~~~~~~~
看来“消息队列中在任何时候只保留一个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&reg;  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事件就不太好了  还是提交给另一个线程吧

#6


因为sleep(1250);的中断等级高于Timer,所以先中断1.25秒再计时,出现这样的问题是当然的。

#7


你可以参考大富翁论坛
关于:"时钟控件定时产生线程操作数据库,shi线程资源释放的问题"
该问题的URL是: http://www.delphibbs.com/delphibbs/dispq.asp?LID=673905
可能会有点启发。

#8


〉〉我的意思是如果你想很精确的每一秒钟执行一个事件是不可能的,timer事件并不是异步执行的
为啥不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秒

#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;

#13


Timer是不准时的!
想可靠就用多媒体定时器
我在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;

#15


同意楼上

#16


flyinwuhan(制怒·三思而后行)  :
我的理解是windows会按照您设定的时间间隔准时的给你发送timer消息,但是如果您的程序处理其他的消息过长,期间windows给你的消息队列发送了数个timer消息,但是队列只保留一个,而忽略其他的。
结果造成两个timer事件的处理间隔不到一秒,也就是说timer是准时的,但是处理事件并不准时。
是这样吧?

#17


#18


>>也就是说timer是准时的,但是处理事件并不准时。
YES

#19


或者说,timer是不是准时的,跟你的程序没有关系,而是取决于系统,如果系统正处理繁忙的任务,就会导致timer不准时。。。。注意,是系统处理繁忙的任务造成timer不准时,而不是你的程序处理繁忙的任务造成timer不准时。。。

>>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,
例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次

这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的消息队列中的。

有些人瞎说。。。误人子弟。。。需要自己做实验才行。。。

#20


我的 主程序都terminate了,但timer里的语句还要执行,导致错误。
timer事件里需要做的事比较多,耗时长,怎么办?

#21


顶一下

#22


flyinwuhan(制怒·三思而后行) 

  >>比如说你的timer每秒钟触发一次,并且如果这个timer事件的处理超过一秒钟的话,  
    例如3秒,那么在这三秒内应该触发三个timer消息,但是消息队列会忽略后两个timer消息,
    只保留第一个,那么最后的结果好像是你的timer三秒钟才触发一次

    这话又是错的,你的程序不会忽略消息队列后两个timer消息,并且两个timer消息是在你的程序的
    消息队列中的

  千真万确,消息队列中在任何时候只保留一个wm_timer消息(当然,如果你定义了两个timer,那就保留两个wm_timer消息。不然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统闲下来,那你这几十个timer就一个挨着一个触发timer事件,这样的话,timer岂不是不准时的太夸张了。

#23


注:…………然的话,按照你的说法,消息处理是异步的了,那么就会存在这种情况:
由于你程序或系统的繁忙,积攒了几十个timer消息,如果突然系统…………

应该是:“timer消息处理是异步的了”

#24


收藏了,有空再研究一下.

#25


niutuoshaozhe(牛拖少这) :
不管您是正常结束程序,还是调用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;

#27


up 一下

#28


呵呵,我的实验有问题。没有深入分析啊~~~~~~~~~~~~
看来“消息队列中在任何时候只保留一个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&reg;  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


多线程