.Net Framework下Timer类之对比

原文:http://msdn.microsoft.com/en-us/magazine/cc164015.aspx

原作者:Alex Calvo,is a Microsoft Certified Solutions Developer for .NET. When he's not reading, coding, or meditating, he's playing guitar. You can reach Alex at  [email protected].

注意: 有删节,仅译出其要点而已,也有自己添加的理解。

概要:
   .NET Framework Class Library提供了三种不同的时钟类:System.Windows.Forms.Timer, System.Timers.Timer  and System.Threading.Timer.各有不同的用武之地,本文旨在介绍其用法。


内容:
       System.Windows.Forms.Timer
       System.Timers.Timer
       System.Threading.Timer
       Dealing with Timer Event Reentrance
       Conclusion


1)System.Windows.Forms.Timer

          该类的最大特点是其与Winforms App同步,也就是说它的Tick events与UI在同一个线程执行。如果在响应函数执行耗时较多的话,则会出现UI无响应的情况。由此可见该类比较适用一些比较简单的情况。
     原文链接中给出了一些演示示例,供读者来理解该类:
System.Windows.Forms.Timer Started @ 4:09:28 PM
--> Timer Event 1 @ 4:09:29 PM on Thread: UIThread
--> Timer Event 2 @ 4:09:30 PM on Thread: UIThread
--> Timer Event 3 @ 4:09:31 PM on Thread: UIThread
Sleeping for 5000 ms...
--> Timer Event 4 @ 4:09:36 PM on Thread: UIThread
System.Windows.Forms.Timer Stopped @ 4:09:37 PM
          从上面代码可以看出,在其响应函数中休眠5000ms,这五秒钟的Timer Event就失效了。足见该Timer Event是在UI线程中执行的。此外要注意的是,尽管该类的Interval 最小可以为1ms,但是官方的文档说明该类大概只能精确到55ms.
   下面代码是使用System.Windows.Forms.Timer class 示例:
System.Windows.Forms.Timer tmrWindowsFormsTimer = new 
    System.Windows.Forms.Timer();
tmrWindowsFormsTimer.Interval = 1000;
tmrWindowsFormsTimer.Tick += new 
    EventHandler(tmrWindowsFormsTimer_Tick);
tmrWindowsFormsTimer.Start();
•••
private void tmrWindowsFormsTimer_Tick(object sender, 
    System.EventArgs e) {
  //Do something on the UI thread...
}


2)System.Timers.Timer

          . Net Framework称该类是专为多线程环境而设计,且该类的对象是thread-safe的。不同于System.Windows.Forms.Timer,该类的Timer Event是在worker thread(来自线程池)中执行的,这就带来一个问题,如果你的响应函数中涉及到UI操作的话,那么一定要回调到UI线程执行。不过,System.Timers.Timer class提供了一种简单的方式来克服这种麻烦,它采用了“SynchronizingObject ”属性,若将UI forms 赋值于该属性,则SynchronizingObject.Begin.Invoke可让Timer Event在UI线程执行。考虑到UI的responsiveness,我觉得这个属性有些鸡肋。
    演示代码:

System.Timers.Timer Started @ 5:15:01 PM
--> Timer Event 1 @ 5:15:02 PM on Thread: WorkerThread
--> Timer Event 2 @ 5:15:03 PM on Thread: WorkerThread
--> Timer Event 3 @ 5:15:04 PM on Thread: WorkerThread
Sleeping for 5000 ms...
--> Timer Event 4 @ 5:15:05 PM on Thread: WorkerThread
--> Timer Event 5 @ 5:15:06 PM on Thread: WorkerThread
--> Timer Event 6 @ 5:15:07 PM on Thread: WorkerThread
--> Timer Event 7 @ 5:15:08 PM on Thread: WorkerThread
--> Timer Event 8 @ 5:15:09 PM on Thread: WorkerThread
System.Timers.Timer Stopped @ 5:15:10 PM
    使用示例:
System.Timers.Timer tmrTimersTimer = new System.Timers.Timer();
tmrTimersTimer.Interval = 1000;
tmrTimersTimer.Elapsed += new 
    ElapsedEventHandler(tmrTimersTimer_Elapsed);
tmrTimersTimer.SynchronizingObject = this; //Synchronize with 
                                           //the current form...
tmrTimersTimer.Start();
•••
private void tmrTimersTimer_Elapsed(object sender, 
    System.Timers.ElapsedEventArgs e) {
  // Do something on the UI thread (same thread the form was 
  // created on)...
  // If we didn't set SynchronizingObject we would be on a 
  // worker thread...
}
    这里有个问题需要注意,由于timer event是在独立线程中执行的,那么有可能用户已经调用stop函数了,仍会产生timer event事件。ElapsedEventArgs 中包含了一个“SignalTime ”属性,表示已经过去的时间,用户可以将这个时间与stop函数的调用时间相比,以决定是否继续执行响应函数。笔者在这里就遇到一个类似的问题,在窗体函数已经销毁后,仍在timer event响应函数中调用Invoke函数,导致了异常,通过该属性则可避免类似情况。

3)System.Threading.Timer
    该类可以说是三个类中最灵活的一个,但是其对象本身却并非thread-safe的。此外,其使用方式也与前二者不同:
public Timer(TimerCallback callback, object state, long dueTime, 
             long period);
public Timer(TimerCallback callback, object state, UInt32 dueTime, 
             UInt32 period);
public Timer(TimerCallback callback, object state, int dueTime, 
             int period);
public Timer(TimerCallback callback, object state, TimeSpan dueTime, 
             TimeSpan period);
    演示代码:
System.Threading.Timer Started @ 7:17:11 AM
--> Timer Event 1 @ 7:17:12 AM on Thread: WorkerThread
--> Timer Event 2 @ 7:17:13 AM on Thread: WorkerThread
--> Timer Event 3 @ 7:17:14 AM on Thread: WorkerThread
Sleeping for 5000 ms...
--> Timer Event 4 @ 7:17:15 AM on Thread: WorkerThread
--> Timer Event 5 @ 7:17:16 AM on Thread: WorkerThread
--> Timer Event 6 @ 7:17:17 AM on Thread: WorkerThread
--> Timer Event 7 @ 7:17:18 AM on Thread: WorkerThread
--> Timer Event 8 @ 7:17:19 AM on Thread: WorkerThread
System.Threading.Timer Stopped @ 7:17:20 AM
    与 System.Timers.Timer class相比较, 该类并没有“SynchronizingObject ”这个属性,也就是说,若在响应函数中涉及UI操作,则需用户自己调用Invoke或者BeginInvoke。

4)Timer event重复进入问题
    对于System.Timers.Timer class和System.Threading.Timer,其timer event异步执行的,还有一个问题需要注意。如果响应函数执行时间超过了Interval值,那么响应函数会被重复进入,那么在这里就特别需要注意变量的同步了。如果读者不想反复进入呢,那么可以参考如下示例:

private void tmrTimersTimer_Elapsed(object sender, 
    System.Timers.ElapsedEventArgs e) {
    tmrTimersTimer.Enabled = false;
    System.Threading.Interlocked.Increment(ref tickCounter);
    Thread.Sleep(5000);
    MessageBox.Show(tickCounter.ToString());
    tmrTimersTimer.Enabled = true;
}
           在响应函数中操作其Enabled属性。

5)总结
Timer Classes in the .NET FCL

System.Windows.Forms System.Timers System.Threading
Timer event 所在线程 UI thread UI or worker thread Worker thread
对象是否thread safe? No Yes No
是否需要Windows Forms? Yes No No
Timer event 是否支持状态变量? No No Yes
初始timer event是否可被延迟? No No Yes
Class 是否支持继承? Yes Yes No

相关文章:
Windows Forms: Give Your .NET-Based Application a Fast and Responsive UI with Multiple Threads

背景信息:
Programming the Thread Pool in the .NET Framework: Using Timers
.NET Framework Class Library

你可能感兴趣的:(thread,.net,timer,UI,object,callback)