Delphi 中的 TTimer 和 .NET Framework 中的 Timer 的计时周期研究

 
Delphi 中的 Timer 的机制究竟是什么样的?

有意查阅了一下 Delphi 的帮助文档,对于 TTimer 类,它的说明如下:

TTimer is used to simplify calling the Windows API timer functions SetTimer and KillTimer, and to simplify processing the WM_TIMER messages. Use one timer component for each timer in the application.

The execution of the timer occurs through its OnTimer event. TTimer has an Interval property that determines how often the timer抯 OnTimer event occurs. Interval corresponds to the parameter for the Windows API SetTimer function.

Caution:    Limitations on the total number of timers system-wide are system dependent.

它清楚地表明它是通过调用  Windows API 函数并处理 WM_TIMER 消息来完成计时功能的。

查看了 TTimer 的源代码,发现 TTimer 组件本身并没有创建线程类,它的机制是通过创建一个窗口句柄,然后将窗口句柄传送给被调用的 Windows API 函数 SetTimer(),SetTimer 会自动地创建一个计时器,并在计时周期过期时向窗口句柄发送 WM_TIMER 消息,而 Timer 控件则从消息泵中捕获该消息,然后调用计时周期的处理事件。也就是说,TTimer 组件所触发的计时事件,实际就是在主线程(UI线程)上执行的事件。它在主线程上执行的事件,不可能并行执行,只会是排序执行的。

Delphi 中,它的关键代码如下:

constructor TTimer.Create(AOwner: TComponent);
begin
  inherited
Create(AOwner);
  FEnabled := True;
  FInterval := 1000;
  //创建句柄,供接收消息使用。
  FWindowHandle := Classes.AllocateHWnd(WndProc);
end;

destructor TTimer.Destroy;
begin
  //销毁句柄
  Classes.DeallocateHWnd(FWindowHandle);
  inherited Destroy;
end;

procedure TTimer.WndProc(var Msg: TMessage);
begin
  //这是消息泵循环的过程,当接收到 WM_TIMER 消息时,立即调用 Timer 方法来触发事件。
  with Msg do
    if
Msg = WM_TIMER then
      try

        Timer;
      except
        Application.HandleException(Self);
      end
    else

      Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
end;

procedure TTimer.UpdateTimer;
begin
  //进入下一个计时器调用周期
  KillTimer(FWindowHandle, 1);
  if (FInterval <> 0) and FEnabled and Assigned(FOnTimer) then
    if
SetTimer(FWindowHandle, 1, FInterval, nil) = 0 then
      raise
EOutOfResources.Create(SNoTimers);
end;

procedure TTimer.Timer;
begin
 
//调用 UI 主线程上的事件,实际就是在组件中编写的事件代码。
  if Assigned(FOnTimer) then FOnTimer(Self);
end;

无独有偶,我查看了 Microsoft 所公布的 .NET 2.0 中 System.Windows.Forms.Timer 的源代码,这个 Timer 组件也是被声明用于 UI 的计时器,发现它同 Delphi 一样是在 Timer 之中建立一个窗口句柄,然后调用 SetTimer,并通过在消息循环中抓取 WM_TIMER 消息来响应计时功能。因此,这种 Timer 只能用于 UI 线程中,而不能用于 Service 服务程序和其它程序,因为它们无法提供窗口句柄的消息循环。 .NET 专门为这些程序提供了另外的两个 Timer 组件,而这两个 Timer 具有更高的精确度。

在 .NET Framework 中,这三种类型的 Timer,存在于不同的命名空间之下。

  • System.Windows.Forms.Timer
  • System.Timer.Timer
  • System.Threading.Timer

我针对前两种 Timer,分别进行了计时测试,以验证应用于窗体的 Timer 和应用于 Service 程序 Timer 之间的区别。测试内容为:

Timer 的计时周期设置为 100ms。在 Timer 中进行随机休眠,这个休眠时间可能 <100ms,更多的是 >100ms,这样就造成了 Timer 的每次执行事件的时长不同。每组进行19次事件测试。

正常情况下,Timer 组件应是 100ms 就会触发一次计时事件。在计时事件中,首先记录本次事件中的初始时间和随机休眠时长,然后使用随机数结果调用 Sleep() 进行指定时长的休眠。以下为测试结果。

System.Windows.Forms.Timer

测试结果见下图。可以看到的是,每次计时周期记录的初始时间基本上是上次的计时周期时间与随机休眠时间之和。也就是说,Timer 的事件是排序的,上一次事件未执行完成时,下次事件不会被触发,而当休眠时间 < 100ms 时,则计时器依照一开始的定点时间进行触发。

image

  

System.Timer.Timer

下图是用 System.Timer.Timer 执行的结果,System.Timer.Timer 是用于 Windows Service 程序的计时器。从下图可以看到,即使 Sleep 延迟超过了 100ms,仍然没有阻碍它定时地触发记录。它每次的执行间隔基本上保持在 100ms 左右,实际上它没有受到事件执行时间长短的影响。

image

综上所述,如果需要精确的计时周期且互不干扰地执行指定的事件,不应当使用基于 UI 的 Timer 组件,而应当选择使用线程或者其它更精确的非 UI Timer 组件。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值