是否将System.Threading.Timer置零?

时间:2010-08-30 01:12:16

标签: c#

如果我有一个活跃的System.Threading.Timer并且我将其设置为null,它是否会停止?

我意识到调用.Dispose()更合适,但我想回答所写的问题。

public class Foo
{
   private System.Threading.Timer _timer;
   public Foo()
   {
      // initialize timer
   }

   public void KillTimer()
   {
      _timer=null;
   }
}

更新

经过多次来回关于是否将System.Threading.Timer的单个引用设置为null确实会导致停止它已经显示

  1. 没有遗留下来的引用,例如事件列表,因为线程计时器需要一个sinlge回调并且不会暴露事件。
  2. 如果 GC收集,终结器将确实处理TimerBase并停止计时器。
  3. using System;
    using System.Threading;
    
    namespace SO_3597276
    {
        class Program
        {
            private static System.Threading.Timer _timer;
    
            static void Main(string[] args)
            {
                _timer = new Timer((s) => Console.WriteLine("fired"), null, 1000, Timeout.Infinite);
                _timer = null;
                GC.Collect();
                Console.ReadKey();
            }
        }
    }
    

    未调用计时器回调。删除GC.Collect()并调用回调。

    谢谢大家。

4 个答案:

答案 0 :(得分:11)

为什么会这样?

考虑:

  System.Threading.Timer t = ...;
  System.Threading.Timer q = t;
  q = null; // Should this stop it as well?

设置为null是对变量执行的操作,而不是对对象执行的操作。 Timer无法知道您将特定变量设置为null,并且无法在此基础上采取操作。

编辑:

为了解决编辑问题,即使在具有唯一引用的情况下,也不能保证定时器将被停止,因为在参考被设置为空之后GC可能无法运行。这也不是完全不可能的,Microsoft .NET实现使用世代收集器,静态字段可能在托儿所集合中存活并被提升为老一代。如果您的程序具有相对稳定的内存配置文件,则可能永远不会有旧代的集合(并且通过扩展,终结器将在程序结束之前运行)。

答案 1 :(得分:7)

我知道你在询问System.Threading.Timer课程,但我想指出一些相当重要的课程。

到目前为止提供的答案都很好。 LoganSLaks是正确的,将任何变量设置为null对先前已为变量分配的对象没有直接影响。 Russell is right当垃圾收集器 最终丢弃计时器时,停止。

SLaks表示在将定时器引用设置为null后,可能会有延迟引用。在一个System.Threading.Timer引用的简单示例中,情况并非如此。

,如果您有System.Timers.Timer,并处理其Elapsed事件,则将其设为null 留下参考,计时器继续永久运行。

因此,请考虑以下代码:

var t = new System.Timers.Timer(1000.0);
t.AutoReset = true;
t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);

Console.Write("Press Enter to start the timer.");
Console.ReadLine();
t.Start();

Console.Write("Press Enter to set t to null.");
Console.ReadLine();

// This will not stop the timer. It actually does nothing at all to the timer
// to which t has been assigned.
t = null;

Console.Write("Press Enter again to perform a garbage collection.");
Console.ReadLine();

// This STILL will not stop the timer, as t was not the only reference to it
// (we created a new one when we added a handler to the Elapsed event).
GC.Collect();

Console.Write("t is null and garbage has been collected. Press Enter to quit.");
Console.ReadLine();

在上面的示例中,由于代码保留了对t的引用以处理其Elapsed事件,因此计时器永远不会停止。

再次,我意识到这不是你问过的课程;我只是提出这一点,指出实际上你是否对某个特定对象有任何更多的引用并不总是很明显。


更新:关于我上面所做的陈述是否同样适用于System.Threading.Timer对象,似乎出现了一些混乱。 它不是。要验证这一点,请考虑以下代码的以下修改:

Console.Write("Press Enter to start the timer.");
Console.ReadLine();

var t = new System.Threading.Timer(
    state => { Console.WriteLine(DateTime.Now); },
    null,
    0,
    1000
);

Console.Write("Press Enter to set t to null.");
Console.ReadLine();

// This will not stop the timer. It actually does nothing at all to the timer
// to which t has been assigned. HOWEVER, if/when the GC comes around to collect
// garbage, it will see that said timer has no active references; and so it will
// collect (and therefore finalize) it.
t = null;

Console.Write("Press Enter again to perform a garbage collection.");
Console.ReadLine();

// This WILL cause the timer to stop, as there is code in the type's
// finalizer to stop it.
GC.Collect();

Console.Write("t is null and garbage has been collected. Press Enter to quit.");
Console.ReadLine();

这就是为什么它不适用于System.Timers.Timer(或任何具有事件的类型)

当我们像这样定义事件处理程序时,很容易错过答案:

t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);

如果我这样定义了我的处理程序,该怎么办?

t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());

哦,对! sender那个没人注意的EventHandler论点!

.NET提供的事件处理基础结构要求对象处理事件维护对引发事件的对象的引用。否则,null代表及其所有表兄弟的签名所提供的合同将被违反。

这个故事的寓意是:只要为事件添加处理程序,就会创建对对象的新引用。在此之后允许该对象被垃圾收集的唯一方法是删除处理程序 - 但如果您将所述对象的唯一其他引用设置为{{1}}(这是其中之一),这可能会非常困难.NET程序可能存在“内存泄漏”的极少数例子。

答案 2 :(得分:6)

不一定。将其设置为null,删除对它的任何引用,并依赖垃圾收集器来处置它。

如果计时器在GC到达之前关闭,它将触发事件。

答案 3 :(得分:2)

不,它不会停止。

将变量设置为null不会直接产生任何副作用,例如停止计时器。 (除非是财产)

由于计时器有其他参考,GC不会收集它,它永远不会停止。