HOWTO · Csharp

在 C# 中重置计时器

本文将讨论如何使用 C# 编程语言重置计时器。

本页内容

要在 C# 中重新开始计时,请对 System.Timers.Timer 调用 Stop(),然后调用 Start()。该类型没有 Reset() 方法。根据需要触发一次还是重复触发 Elapsed 事件来设置 AutoReset;此属性不会按需重启正在进行的倒计时。

你需要哪种 C# 计时行为?

如果某项操作应在延迟后只执行一次,或由代码明确安排下一次计时,请使用单次计时器。将 AutoReset 设为 false,需要重新开始计时区间时再调用 Stop() 和 Start()。对于周期性任务,将 AutoReset 设为 true,让计时器在每个计时间隔后触发 Elapsed。

如果计时器正在运行,而你想推迟下一次事件,请先停止再启动它。对已启用的计时器调用启动操作,并不会提供独立的重置功能。计时间隔以毫秒为单位;请根据任务选择正数时长。计时器用于安排回调,不适合测量精确的经过时间。

System.Timers.Timer 如何处理经过时间事件

System.Timers.Timer 会在达到设定的 Interval 后触发 Elapsed 事件。Enabled 表示计时器是否正在运行;Start() 会启用计时器,Stop() 会将其停用。事件处理程序会收到一个 ElapsedEventArgs 值,其中包含信号时间。处理程序异步运行,因此周期性任务耗时超过计时间隔时,多个处理程序可能重叠执行。请妥善保护共享状态,也不要假定回调会在 UI 线程上运行。

AutoReset 是布尔属性。设为 true 时,计时器会按设定间隔安排后续事件;设为 false 时,计时器触发一次事件后便会停止。此属性控制是否重复触发,并不是重启当前倒计时的命令。单次计时器需要再计时一次时,应在处理完事件后明确调用 Start()。

使用 Stop 和 Start 重启单次计时器

对于正在运行的单次计时器,调用 Stop() 取消待执行的计时安排,然后调用 Start() 开始新的计时间隔。这就是实际可用的重置方式。如果计时器已在 AutoReset 设为 false 时触发过事件,调用 Start() 即可再次安排计时。若想从一开始就明确计时器的行为,请在启动前设置 AutoReset。

请记住,计时器回调是异步的。程序的其他部分停止计时器时,回调可能已经进入队列;在这种情况下,请妥善协调关闭过程并保护共享数据。处理程序应尽量简短;耗时较长的工作可以交给受控的工作线程,避免周期性触发与尚未完成的任务相互争用。

使用 AutoReset 安排周期性计时

对于轮询或定期刷新数值等周期性任务,将 AutoReset = true。每次经过设定的计时间隔后,计时器都会安排一个事件。需要只触发一次并由代码明确决定何时开始下一段计时时,将其设为 false。这两种设置都不会提供倒计时数值,也不会就地更改已安排的下一次触发间隔;要安排全新的计时间隔,请先停止再启动计时器。

不要把周期性计时器当作精确时钟。线程池负载可能导致调度延迟,回调也未必会恰好在指定的毫秒数开始。如果每次操作都必须在下一次开始前完成,可以在操作结束后再安排一次单次计时,或为任务添加同步机制。

安全地停止并释放计时器

计时器不应再安排事件时,调用 Stop()。所属操作结束后,调用 Dispose() 释放计时器资源。停止和释放用途不同:Stop() 结束当前的计时安排,Dispose() 则结束计时器的生命周期。不要重新启动已释放的计时器;如果后续操作还需要计时器,请创建新实例。

由于 Elapsed 是异步运行的,停止计时器并不会让已经开始执行的处理程序消失。如果关闭过程必须等待回调完成,请使用适合应用程序的同步机制与处理程序协调。只要处理程序仍可能使用计时器,就应让计时器保持有效;不再需要长期存在的计时器时,也应取消订阅其事件处理程序。

完整示例:重启单次计时器

这个完整示例会在两秒后触发一次事件,在该处理程序中重新开始一个计时间隔,并在第二次事件后退出。将 AutoReset = false 设为单次触发;通过 Stop() 和 Start() 明确安排第二段计时。完成信号让 Main 等待两次回调执行完毕后再进行清理。

运行此示例:

本节中的代码以及显示的标签或输出仍为英文。

using System;
using System.Threading;
using System.Timers;

class TimerRestartExample
{
    static void Main()
    {
        using (var completed = new ManualResetEventSlim())
        using (var timer = new System.Timers.Timer(2000))
        {
            int eventCount = 0;
            timer.AutoReset = false;
            timer.Elapsed += (sender, args) =>
            {
                eventCount++;
                Console.WriteLine("Elapsed event " + eventCount);

                if (eventCount == 1)
                {
                    timer.Stop();
                    timer.Start();
                }
                else
                {
                    completed.Set();
                }
            };

            timer.Start();
            completed.Wait();
            timer.Stop();
        }
    }
}
Elapsed event 1
Elapsed event 2

提供的运行记录显示,示例成功完成,并打印了上方所示的两次经过时间事件。示例代码和输出区块中的可见文本保持英文原样。在实际应用中,应根据应用的生命周期选择计时间隔和完成策略;服务或 UI 可能会在计时器事件后继续运行,而不会像此演示那样等待后退出。