Sco*_*ain 24 c# multithreading garbage-collection timer
当您使用a 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();
}
Run Code Online (Sandbox Code Playgroud)
计时器是否可以在上面的示例中收集?我跑了一段时间,我从来没有得到过例外,也没有一条消息说~Program()被叫.
更新:我从这个问题(感谢sethcran)中发现线程被CLR跟踪,但我仍然想要一个关于Timers的答案.
Han*_*ant 48
这只是System.Threading.Timer类的一个问题,如果你没有以其他方式存储对它的引用.它有几个构造函数重载,采用状态对象的重载很重要.CLR关注该状态对象.只要在某处引用它,CLR就会将计时器保留在其计时器队列中,并且计时器对象不会被垃圾收集.大多数程序员都不会使用那个状态对象,MSDN文章肯定不解释它的作用.
System.Timers.Timer是System.Threading.Timer类的包装器,使其更易于使用.特别是,只要启用了计时器,它就会使用该状态对象并保持对它的引用.
请注意,在您的情况下,计时器的Enabled属性在进入Elapsed事件处理程序时为false,因为您具有AutoReset = false.因此,一旦计时器进入您的事件处理程序,它就有资格收集.但是你可以通过引用sender参数来避免麻烦,需要将Enabled设置为true.这使得抖动报告成为参考,因此您没有问题.
小心Elapsed事件处理程序.在没有诊断的情况下,将吞下该方法内引发的任何异常.这也意味着您不会将Enabled设置为true.你必须使用try/catch来做一些合理的事情.如果你不打算故意结束你的程序,至少你需要让你的主程序知道某些东西不再起作用了.在finally子句中放置Enabled = true可以避免收集计时器垃圾,但是可能会让程序一次又一次地抛出异常.
我们来做个实验:
private static void UnderTest()
{
// Timer is a local variable; its callback is local as well
System.Threading.Timer timer = new System.Threading.Timer(
(s) => { MessageBox.Show("Timer!"); },
null,
1000,
1000);
}
Run Code Online (Sandbox Code Playgroud)
...
// Let's perform Garbage Collection manually:
// we don't want any surprises
// (e.g. system starting collection in the middle of UnderTest() execution)
GC.Collect(2, GCCollectionMode.Forced);
UnderTest();
// To delay garbage collection
// Thread.Sleep(1500);
// To perform Garbage Collection
// GC.Collect(2, GCCollectionMode.Forced);
Run Code Online (Sandbox Code Playgroud)
迄今为止
Thread.Sleep(1500);,GC.Collect(2, GCCollectionMode.Forced);我们会看到消息框出现:计时器正在工作GC.Collect(2, GCCollectionMode.Forced);我们将看不到任何内容:计时器启动然后收集Thread.Sleep(1500);,GC.Collect(2, GCCollectionMode.Forced);我们将看到一个消息框:计时器启动,关闭一个消息框,然后收集计时器因此System.Threading.Timers 会像任何其他对象实例一样被收集。