2016-06-07 43 views
8

根据MSDN,应该保留对System.Threading.Timer的引用,否则它将被垃圾收集。所以,如果我运行此代码,它不写任何信息(这是预期的行为):C#定时器和垃圾回收

static void Main(string[] args) 
{ 
    RunTimer(); 
    GC.Collect(); 
    Console.ReadKey(); 
} 

public static void RunTimer() 
{ 
    new Timer(s => Console.WriteLine("Hello"), null, TimeSpan.FromSeconds(1), TimeSpan.Zero); 
} 

但是,如果我通过存储计时器在一个临时局部变量稍微修改了代码,它生存和写消息:

public static void RunTimer() 
{ 
    var timer = new Timer(s => Console.WriteLine("Hello")); 
    timer.Change(TimeSpan.FromSeconds(1), TimeSpan.Zero); 
} 

在垃圾收集期间,显然没有办法如何从根或静态对象访问定时器。那么你能解释为什么计时器能够存活吗?参考文献保存在哪里?

+6

这当然*使用*为真。看起来微软终于做了一些事情,数以千计的支持电话肯定是鼓舞人心的。不知道什么时候发生,我怀疑是4.5或4.6左右。他们现在保持活着状态,[TimerQueue.s_queue](http://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,75523a07eb2de983)照顾它。 –

+3

谢谢,但那么应该保持计时器在两种情况下都活着?为什么只能存活第二个?我正在运行.NET 4.6.1。 –

+1

这看起来像一个bug。只有Change()方法会将计时器添加到计时器队列中,构造函数忘记了这么做。很难想象这是故意的,请考虑报告。 –

回答

3

每个Timer参考TimerHolder,其中引用TimerQueueTimer。该实现通过UpdateTimer()的调用保留对TimerQueueTimer的内部参考。

在正常情况下,您的计时器可以自由收集,finalizing the TimerHolder并从内部队列中删除TimerQueueTimer。但简单的构造函数Timer(TimerCallback)calls TimerSetup()Timer本身作为状态。所以在这种情况下,TimerQueueTimer的状态引用回Timer,阻止它被收集。

该效果与保留临时局部变量无关。它只是发生工作,由于Timer机制的内部。按照MSDN的建议保持对定时器的引用会更清晰和更安全。