定时器可以自动收集垃圾吗?

当你使用一个Timer或一个只运行整个程序生命周期的Thread ,你是否需要保留对它们的引用以防止它们被垃圾收集?

请不要理会下面的程序可以将timer作为类中的静态变量,这只是一个展示问题的玩具示例。

 public class Program { static void Main(string[] args) { CreateTimer(); Console.ReadLine(); } private static void CreateTimer() { var program = new Program(); var timer = new Timer(); timer.Elapsed += program.TimerElapsed; timer.Interval = 30000; timer.AutoReset = false; timer.Enabled = true; } private void TimerElapsed(object sender, ElapsedEventArgs e) { var timerCast = (Timer)sender; Console.WriteLine("Timer fired at in thread {0}", GetCurrentThreadId()); timerCast.Enabled = true; } ~Program() { Console.WriteLine("Program Finalized"); } [DllImport("kernel32.dll")] static extern uint GetCurrentThreadId(); } 

计时器是否可以在上面的示例中收集? 我运行了一段时间,我从来没有得到exception,也没有消息说~Program()调用了~Program()

更新:我从这个问题 ( 感谢sethcran )发现线程被CLR跟踪,但我仍然想要一个关于Timers的答案。

这只是System.Threading.Timer类的一个问题,如果你没有以其他方式存储对它的引用。 它有几个构造函数重载,采用状态对象的重载很重要。 CLR关注该状态对象。 只要在某处引用它,CLR就会将计时器保留在其计时器队列中,并且计时器对象不会被垃圾收集。 大多数程序员都不会使用那个状态对象,MSDN文章肯定不解释它的作用。

System.Timers.Timer是System.Threading.Timer类的包装器,使其更易于使用。 特别是,只要启用了计时器,它就会使用该状态对象并保持对它的引用。

请注意,在您的情况下,计时器的Enabled属性在进入Elapsed事件处理程序时为false,因为您具有AutoReset = false。 因此,一旦进入事件处理程序,计时器就有资格进行收集。 但是,您可以通过引用sender参数来避免麻烦,需要将Enabled设置为true。 这使得抖动报告成为参考,因此您没有问题。

小心Elapsed事件处理程序。 在没有诊断的情况下吞下该方法中引发的任何exception。 这也意味着您不会将Enabled设置为true。 你必须使用try / catch来做一些合理的事情。 如果你不打算故意结束你的程序,至少你需要让你的主程序知道某些东西不再起作用了。 在finally子句中放置Enabled = true可以避免收集计时器垃圾,但可能会让程序一次又一次地抛出exception。

将此代码添加到程序并运行它。 您将看到未收集计时器。

  private void DoStuff() { CreateTimer(); Console.WriteLine("Timer started"); int count = 0; for (int x = 0; x < 1000000; ++x) { string s = new string("just trying to exercise the garbage collector".Reverse().ToArray()); count += s.Length; } Console.WriteLine(count); Console.Write("Press Enter when done:"); Console.ReadLine(); } private void Ticktock(object s, System.Timers.ElapsedEventArgs e) { Console.WriteLine("Ticktock"); } private void CreateTimer() { System.Timers.Timer t = new System.Timers.Timer(); // Timer(Ticktock, null, 1000, 1000); t.Elapsed += Ticktock; t.Interval = 1000; t.AutoReset = true; t.Enabled = true; } 

因此,您的问题的答案似乎是计时器不符合收集条件,如果您不保留对它的引用,则不会收集计时器。

有趣的是,如果你使用System.Threading.Timer运行相同的测试,你会发现收集了计时器。