.NET中System.Diagnostics.Stopwatch、System.Timers.Timer、System.Threading.Timer 的区别

时间:2022-01-08 09:01:49

1.System.Diagnostics.Stopwatch

Stopwatch 实例可以测量一个时间间隔的运行时间,也可以测量多个时间间隔的总运行时间。 在典型的 Stopwatch 方案中,先调用 Start 方法,然后调用 Stop 方法,最后使用 Elapsed 属性检查运行时间。Stopwatch 实例或者在运行,或者已停止;使用 IsRunning 可以确定 Stopwatch 的当前状态。 使用 Start 可以开始测量运行时间;使用 Stop 可以停止测量运行时间。 通过属性 Elapsed、ElapsedMilliseconds 或 ElapsedTicks 查询运行时间值。 当实例正在运行或已停止时,可以查询运行时间属性。 运行时间属性在 Stopwatch 运行期间稳固递增;在该实例停止时保持不变。默认情况下,Stopwatch 实例的运行时间值相当于所有测量的时间间隔的总和。 每次调用 Start 时开始累计运行时间计数;每次调用 Stop 时结束当前时间间隔测量,并冻结累计运行时间值。 使用 Reset 方法可以清除现有 Stopwatch 实例中的累计运行时间。Stopwatch 在基础计时器机制中对计时器的计时周期进行计数,从而测量运行时间。 如果安装的硬件和操作系统支持高分辨率性能计数器,则 Stopwatch 类将使用该计数器来测量运行时间; 否则,Stopwatch 类将使用系统计数器来测量运行时间。 使用 Frequency 和 IsHighResolution 字段可以确定实现 Stopwatch 计时的精度和分辨率。

Stopwatch 类为托管代码内与计时有关的性能计数器的操作提供帮助。 具体说来,Frequency 字段和 GetTimestamp 方法可以用于替换非托管 Win32 API QueryPerformanceFrequency 和 QueryPerformanceCounter。

2.System.Timers.Timer

System.Timers.Timer组件是基于服务器的计时器,它使您能够指定在应用程序中引发 Elapsed 事件的周期性间隔。 然后可通过处理这个事件来提供常规处理。 例如,假设您有一台关键性服务器,必须每周 7 天、每天 24 小时都保持运行。 可以创建一个使用 Timer 的服务,以定期检查服务器并确保系统开启并在运行。 如果系统不响应,则该服务可以尝试重新启动服务器或通知管理员。基于服务器的 Timer 是为在多线程环境中用于辅助线程而设计的。 服务器计时器可以在线程间移动来处理引发的 Elapsed 事件,这样就可以比 Windows 计时器更精确地按时引发事件。基于 Interval 属性的值,Timer 组件引发 Elapsed 事件。 您可以处理这个事件来执行必要的处理。 例如,假设您有一个联机销售应用程序,它持续向一个数据库投递销售订单。 编译要传送的指令的服务成批处理订单,而不是单独地处理每份订单。 可以使用 Timer 每 30 分钟启动一次批处理。

说明

当 AutoReset 设置为 false 时,Timer 只在第一个 Interval 过后引发一次 Elapsed 事件。 若要保持以 Interval 时间间隔引发 Elapsed 事件,请将 AutoReset 设置为 true。

在 .NET Framework 2.0 版及更早版本中,Timer 组件捕获 Elapsed 事件的事件处理程序所引发的所有异常并禁止显示这些异常。 在将来的 .NET Framework 版本中,此行为可能会有更改。如果 SynchronizingObject 属性为 null,则在 ThreadPool 线程上引发 Elapsed 事件。 如果 Elapsed 事件的处理时间比 Interval 长,在另一个 ThreadPool 线程上将会再次引发此事件。 在这种情况下,事件处理程序应当是可重入的。一个线程调用 Stop 方法或将 Enabled 属性设置为 false 的同时,可在另一个线程上运行事件处理方法。 这可能导致在计时器停止之后引发 Elapsed 事件。 Stop 方法的代码示例演示了一种避免此争用条件的方法。

即使 SynchronizingObject 不是 null,Elapsed 在 Dispose 或 Stop 方法调用后或 Enabled 属性设置为 false 后,事件可以发生,这是因为引出 Elapsed 事件的信号总是排队等待执行线程池线程。 解决此争用条件的一种方法是:设置一个标志,通知 Elapsed 事件的事件处理程序忽略随后的事件。如果您使用 Timer 与用户界面元素如窗体或控件,没有放置计时器的该用户界面元素,指定包含 Timer 窗体或控件到 SynchronizingObject 属性,以使该事件封送到用户界面线程。Timer 在运行时是不可见的。

3.System.Threading.Timer

使用 TimerCallback 委托指定希望 Timer 执行的方法。 计时器委托在构造计时器时指定,并且不能更改。 此方法不在创建计时器的线程上执行,而是在系统提供的 ThreadPool 线程上执行。创建计时器时,可以指定在第一次执行方法之前等待的时间量(截止时间)以及此后的执行期间等待的时间量(时间周期)。 可以使用 Change 方法更改这些值或禁用计时器。

说明

只要在使用 Timer,就必须保留对它的引用。 对于任何托管对象,如果没有对 Timer 的引用,计时器会被垃圾回收。 即使 Timer 仍处在活动状态,也会被回收。

当不再需要计时器时,请使用 Dispose 方法释放计时器持有的资源。 请注意,回调可能发生在 Dispose() 方法的重载被调用之后,因为计时器是按线程池线程来安排回调的执行的。 可使用 Dispose(WaitHandle) 方法重载进行等待,直到完成所有的回调。

由计时器执行的回调方法应该是可重入的,因为它是在 ThreadPool 线程上调用的。 在以下两种情况中,此回调可以同时在两个线程池线程上执行:一是计时器间隔小于执行此回调所需的时间;二是所有线程池线程都在使用,此回调被多次排队。System.Threading.Timer 是一个简单的轻量计时器,它使用回调方法并由线程池线程提供服务。 不建议将其用于 Windows 窗体,因为其回调不在用户界面线程上进行。 System.Windows.Forms.Timer 是用于 Windows 窗体的更佳选择。 要获取基于服务器的计时器功能,可以考虑使用 System.Timers.Timer,它可以引发事件并具有其他功能。