是否将System.Threading.Timer置零?

Sky*_*ers 13 c#

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

我意识到打电话更合适 .Dispose()但我想回答问题的答案.

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

   public void KillTimer()
   {
      _timer=null;
   }
}
Run Code Online (Sandbox Code Playgroud)

更新:

关于是否将System.Threading.Timer的单个引用设置为null之后来回确定是否会停止显示

  1. 没有挥之不去的引用,例如事件列表,因为线程计时器采用sinlge回调并且不公开事件.
  2. 如果 GC收集,终结确实会处置TimerBase和停止计时器.

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢大家.

Log*_*ldo 11

为什么会这样?

考虑:

  System.Threading.Timer t = ...;
  System.Threading.Timer q = t;
  q = null; // Should this stop it as well?
Run Code Online (Sandbox Code Playgroud)

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

编辑:

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

  • "正确编写的程序不能依赖终结器的副作用." http://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx (3认同)

Dan*_*Tao 7

我知道你在问这个System.Threading.Timer班级,但我想指出一些相当重要的东西.

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

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

但是,如果您有,例如,a 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();
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,由于有代码保持引用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();
Run Code Online (Sandbox Code Playgroud)

这就是为什么它不起作用System.Timers.Timer(或任何具有事件的类型,实际上):

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

t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
Run Code Online (Sandbox Code Playgroud)

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

t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());
Run Code Online (Sandbox Code Playgroud)

啊对!那个sender没有人关注过的论点!

.NET提供的事件处理基础结构要求处理事件的对象维护对引发事件的对象的引用.否则,EventHandler代表及其所有表兄弟的签名所提供的合同将受到侵犯.

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


Rus*_*ell 6

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

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