C#——Timer的实例何时会被终止

本文关键字:终止 何时会 Timer 实例 | 更新日期: 2023-09-27 17:57:55

给定以下代码,计时器将每隔一秒钟触发一次PrintTimer函数,直到用户按键。

问题1>计时器何时停止?计时器在默认情况下是后台线程吗?因此它将在前台线程终止后被终止。

问题2>如何以编程方式停止调用线程中的计时器?

class Program
{
    // continue to call this function until the user presses a 
    // key to terminate the application
    static void PrintTime(object state) 
    {
        Console.WriteLine("Time is: {0}",
        DateTime.Now.ToLongTimeString());
    }
    static void Main(string[] args)
    {
        TimerCallback timeCB = new TimerCallback(PrintTime);
        System.Threading.Timer t = new Timer(
            timeCB,
            null,
            0,
            1000);
        Console.WriteLine("Hit key to terminate...");
        Console.ReadLine();
    }
}

C#——Timer的实例何时会被终止

答案1

现在,只有当程序退出时,计时器才会停止。它并不是真正使用后台线程,但当计时器事件发生时,回调将通过ThreadPool线程调用,后台线程。

答案2

停止计时器很简单:

t.Change(Timeout.Infinite, Timeout.Infinite);

--或--

t.Dispose();

前者允许您通过另一个对Change的调用重新启动Timer,后者将完全清除Timer

有关其行为的更多详细信息,我建议阅读MSDN文档中Timer类的"备注"部分。例如,Dispose有一些棘手的时间安排,因为Timer可能已经将通知回调排队到线程池,即使在调用Dispose之后,从技术上讲也可以接收到该通知回调。

计时器将通过finzalizer运行进行清理。这就是它变成garabage的时候,它实际上因发布模式和调试模式而异。

下面是一个我们可以使用WeakReference运行的测试:

internal class Program
{
    // continue to call this function until the user presses a 
    // key to terminate the application
    private static void PrintTime(object state)
    {
        Console.WriteLine("Time is: {0}",
                          DateTime.Now.ToLongTimeString());
    }
    private static void Main(string[] args)
    {
        TimerCallback timeCB = new TimerCallback(PrintTime);
        Timer t = new Timer(
            timeCB,
            null,
            0,
            1000);
        WeakReference wk = new WeakReference(t);
        PrintStatus(wk);
        Console.WriteLine("Hit key to terminate...");
        Console.ReadLine();
        t = null;
        PrintStatus(wk);
    }
    private static void PrintStatus(WeakReference wk)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Is timer alive? " + wk.IsAlive);
    }
}

Timer是一次性的,因此您应该/必须对其调用Dispose。如果您想重用它,也可以调用Stop,但Timer类中存在一个带有定期回调的错误,因此建议只处理它并从新计时器开始。

在你发布的代码中会发生的事情是,定时器的终结器将(从技术上讲可能)被调用,这将杀死它。

在您的应用程序中,它永远不会停止,直到您退出应用程序。

要用程序停止它,这取决于你使用的计时器。

System.Timers.Timer
System.Threading.Timer

如果是第一个,则可以调用Stop方法。如果是第二个,你必须处理()它。

请注意,如果您在一个方法中启动计时器,并且不保留对它的引用,那么当它被垃圾收集时,它将被停止。

关于问题2,请参阅Timer.Enabled属性和Timer.Close()方法

更新:我想为了完整起见,我还应该提到Timer.Stop()方法(它将Enabled设置为false)

更新:每个选项应该在什么时候使用?

广义上讲,有两种情况:要么你正在取消计时器,再也不打算使用它,要么你正在暂停计时器,但计划在某个时候恢复它(或者至少希望选项能够恢复它)。

如果您要取消计时器,那么Dispose/Close就是您要采取的路线。至少您应该调用Dispose,因为当一个行为良好的程序完成一个实现IDisposable的对象时,它应该这样做。

也就是说,当一个对象同时实现IDisposable和Close方法时,我认为更好的方法是调用Close,因为Close可能做的不仅仅是释放资源。例如,它可能会刷新缓冲区或做一些其他特定于对象的事情。可以期望Close方法调用Dispose。理想情况下,Close的文档会明确说明它调用Dispose(但Timer::Close的文件没有说明它调用了Dispose)。

要临时暂停和恢复计时器,可以将Enabled属性设置为false,然后再次设置为true。或者,你可以调用Stop(),然后调用Start()来恢复它。

我想有人可能会说,"停止"answers"启动"的可读性稍高,效率可能稍低(因为它们依次设置为"启用")。