System.Threading.Timer是最简单的多线程定时器:它只有一个构造器和两个方法(尤其适合于像作者这样的极简主义者!)。在接下来的例子中,定时器会在第一个五秒钟结束后调用Tick方法,并输出“tick…”。而后每一秒钟调用一次Tick方法,直至用户按下回车键:
using System;
using System.Threading;
class Program
{
static void Main()
{
// First interval = 5000ms; subsequent intervals = 1000ms
Timer tmr = new Timer (Tick, "tick...", 5000, 1000);
Console.ReadLine();
tmr.Dispose(); // This both stops the timer and cleans up.
}
static void Tick (object data)
{
// This runs on a pooled thread
Console.WriteLine (data); // Writes "tick..."
}
}
12.5.1节中介绍了如何销毁一个多线程定时器。
在创建定时器之后仍然可以调用Change方法修改定时器的定时间隔。如果希望定时器只触发一次,则可以用Timeout.Infinite作为构造器的最后一个参数。
.NET Framework在System.Timers命名空间中提供了另外一个同名定时器类。它简单包装了System.Threading.Timer,在相同底层引擎的基础上提供了额外的易用性。以下总结了它的附加功能:
·实现了IComponent接口,允许嵌入到Visual Studio设计器的组件托盘中。
·提供了Interval属性替代Change方法。
·提供了Elapsed事件取代回调委托。
·提供了Enabled属性来开始和停止计时器(默认值为false)。
·如果不习惯使用Enabled属性还可以使用Start和Stop方法。
·提供了AutoReset标志,用于指示重复的事件(默认值为true)。
·提供了SynchronizingObject属性。可调用该对象的Invoke和BeginInvoke方法安全地调用WPF元素和Windows Forms控件的方法。
例如:
using System;
using System.Timers; // Timers namespace rather than Threading
class SystemTimer
{
static void Main()
{
Timer tmr = new Timer(); // Doesn't require any args
tmr.Interval = 500;
tmr.Elapsed += tmr_Elapsed; // Uses an event instead of a delegate
tmr.Start(); // Start the timer
Console.ReadLine();
tmr.Stop(); // Stop the timer
Console.ReadLine();
tmr.Start(); // Restart the timer
Console.ReadLine();
tmr.Dispose(); // Permanently stop the timer
}
static void tmr_Elapsed (object sender, EventArgs e)
{
Console.WriteLine ("Tick");
}
}
多线程定时器会使用线程池来用有限的线程为多个定时器提供服务。因此,回调方法或者Elapsed事件每一次都可能在不同的线程上触发。此外,Elapsed事件几乎能够保证触发的时效性而不管前一次Elapsed事件是否执行完毕。因此,不论是回调委托还是事件处理器必须是线程安全的。
多线程的定时器精度取决于操作系统,一般情况下精度在10到20毫秒范围内。如果需要更高的精度,则可以使用原生的互操作并调用Windows的多媒体定时器。多媒体定时器定义在winmm.dll中,且其精度接近1毫秒。首先调用timeBeginPeriod通知操作系统提高定时精度,然后调用timeSetEvent方法启动一个多媒体定时器。当使用完毕之后,调用timeKillEvent停止定时器,并调用timeEndPeriod通知操作系统不再需要提高定时精度了。