定期执行受计算限制的异步操作
System.Threading命名空间中定义了一个Timer类,可使用这个类让CLR定期地调用方法。构建Timer类的实例时,实际是在告诉CLR我们希望一个方法在指定的时间内被再次调用。Timer类提供了几个非常相似的构造器:
public sealed class Timer : MarshalByRefObject, IDisposable
{
public Timer(TimerCallback callback, Object state, int dueTime, int period);
public Timer(TimerCallback callback, Object state, UInt32 dueTime, UInt32 period);
public Timer(TimerCallback callback, Object state, UInt64 dueTime, UInt64 period);
public Timer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period);
}
delegate void TimerCallback(object state);
state参数允许我们将状态数据传递给回调方法。dueTime参数指定在第一次调用回调方法时需要等待多少毫秒。period参数指定回调方法被调用的时间间隔(以毫秒为单位)。如果该参数传递的值为Timeout.Infinite(-1),那么线程池中的线程只会调用回调方法一次。
从内部实现上讲,所有的Timer对象只使用CLR的一个线程,该线程知道下一个Timer对象什么时候到达。当下一个Timer对象到达后,CLR的线程就会醒来,并在其内部调用ThreadPool的QueueUserWorkItem方法将一个条目加入到线程池的队列中,从而导致回调方法被调用。如果回调方法需要较长的时间来执行,那么定时器可能会再次触发。这可能导致多个线程池中的对象同时执行回调方法。请注意这一点,如果我们的方法访问了任何共享数据,就需要增加一些线程同步锁来防止数据被破坏。
Timer类还提供了一些额外的方法(Change和Dispose),当方法被回调时,这些方法允许我们与CLR进行通信从而对定时器进行修改。Change方法允许我们更改或重新设置Timer对象的启动时间和间隔。Dispose方法允许我们完全取消定时器,还可以在所有挂起的回调方法完成时选择性的通知notifyObject参数标识的内核对象。
当Timer对象被执行垃圾收集时,CLR就会取消定时器,以便定时器不再继续触发。因此,在使用Timer对象时,一定要确保有一个变量来引用Timer对象,从而保持Timer对象继续存活,否则回调方法就不会再被调用。
下面的代码演示了Timer的使用方法:
using System;
using System.Threading;
public static class Program
{
public static void Main()
{
Console.WriteLine("Main thread: starting a timer");
Timer t = new Timer(ComputeBoundOp, 5, 0, 2000);
Console.WriteLine("Main thread: Doing other work here...");
Thread.Sleep(10000); //模拟其他工作10秒
t.Dispose(); //取消定时器
}
//该方法签名必须与TimerCallback委托类型匹配
private static void ComputeBoundOp(object state)
{
//该方法由线程池中的线程执行
Console.WriteLine("In ComputeBoundOp: state = {0}", state);
Thread.Sleep(1000); //模拟其他工作1秒
//方法返回后,线程就回到线程池中,然后等待执行另一个任务
}
}
三个定时器的史话
FCL中实际提供了三个定时器。
System.Threading的Timer类:该定时器就是前一节所讨论的定时器,希望在另一个线程上定时执行后台任务时,这个定时器是最好的定时器。
System.Windows.Forms的Timer类:构建一个该类的实例可以告诉Windows将定时器与调用线程关联。随着定时器的触发,Windows将一个定时器消息(WM_TIMER)插入到线程的消息队列中。调用线程必须执行一个消息泵,从而提取消息,并将它们分派到期望的回调方法中。注意,所有这些工作都是由一个线程完成的--设置定时器的线程保证是执行回调方法的线程。这同样意味着我们的定时器方法不能被多个线程同时执行。
System.Timers的Timer类:该定时器基本上是对System.Threading的Timer类的包装,当定时器时间到期后,将导致CLR将事件加入线程池的队列中。System.Timers.Timer类派生自System.ComponentModel的Component类,Component类允许将这些定时器对象放置在VS设计界面上。不建议使用该类,因为该类今后有可能被移除。