问题描述
当您使用将在程序的整个生命周期内运行的 Timer
或 Thread
时,您是否需要保留对它们的引用以防止它们被垃圾收集了吗?
When you use a Timer
or a Thread
that will just run for the entire lifetime of the program do you need to keep a reference to them to prevent them from being garbage collected?
请抛开下面的程序可以将 timer
作为类中的静态变量这一事实,这只是一个展示问题的玩具示例.
Please put aside the fact that the below program could have timer
as a static variable in the class, this is just a toy example to show the issue.
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();
}
可以在上面的示例中收集计时器吗?我运行了一段时间,我从来没有收到异常,也没有消息说 ~Program()
被调用了.
Could the timer get collected in that above example? I ran it for a while and I never got a exception nor a message saying ~Program()
was called.
更新:我从 这个问题中发现(感谢sethcran)线程由CLR跟踪,但我仍然想要关于计时器的答案.
UPDATE: I found out from this question (thanks sethcran) that threads are tracked by the CLR, but I still would like an answer about Timers.
推荐答案
这只是 System.Threading.Timer 类的问题,如果您不在某处存储对它的引用.它有几个构造函数重载,采用 state 对象的重载很重要.CLR 关注该状态对象.只要在某处引用它,CLR 就会将计时器保留在其计时器队列中,并且计时器对象不会被垃圾回收.大多数程序员不会使用那个状态对象,MSDN 文章肯定没有解释它的作用.
This is only a problem with the System.Threading.Timer class if you don't otherwise store a reference to it somewhere. It has several constructor overloads, the ones that take the state object are important. The CLR pays attention to that state object. As long as it is referenced somewhere, the CLR keeps the timer in its timer queue and the timer object won't get garbage collected. Most programmers will not use that state object, the MSDN article certainly doesn't explain its role.
System.Timers.Timer 是 System.Threading.Timer 类的包装器,使其更易于使用.特别是,只要启用了计时器,它将使用该状态对象并保持对它的引用.
System.Timers.Timer is a wrapper for the System.Threading.Timer class, making it easier to use. In particular, it will use that state object and keep a reference to it as long as the timer is enabled.
请注意,在您的情况下,计时器的 Enabled 属性在进入您的 Elapsed 事件处理程序时为 false,因为您有 AutoReset = false.因此,一旦计时器进入您的事件处理程序,它就有资格被收集.但是您可以通过引用 sender 参数来避免麻烦,需要将 Enabled 设置回 true.这使得抖动报告参考,所以你没有问题.
Note that in your case, the timer's Enabled property is false when it enters your Elapsed event handler because you have AutoReset = false. So the timer is eligible for collection as soon as it enters your event handler. But you stay out of trouble by referencing the sender argument, required to set Enabled back to true. Which makes the jitter report the reference so you don't have a problem.
请小心使用 Elapsed 事件处理程序.该方法中抛出的任何异常都将在没有诊断的情况下被吞下.这也意味着您不会将 Enabled 设置回 true.您必须使用 try/catch 来做一些合理的事情.如果你不打算故意结束你的程序,至少你需要让你的主程序知道某些东西不再工作了.将 Enabled = true 放在 finally 子句中可以避免收集计时器垃圾,但有可能让您的程序一遍又一遍地抛出异常.
Do be careful with the Elapsed event handler. Any exception thrown inside that method will be swallowed without a diagnostic. Which also means that you won't set Enabled back to true. You must use try/catch to do something reasonable. If you are not going to intentionally end your program, at a minimum you'll need to let your main program know that something isn't working anymore. Putting Enabled = true in a finally clause can avoid getting the timer garbage collected, but at the risk of having your program throw exceptions over and over again.
这篇关于计时器可以自动垃圾收集吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!