具体来说,如果在本地范围内创建Timer实例,然后从该范围返回:
1)计时器是否仍然执行?
2)什么时候会被垃圾收集?
我提供以下两种情况:
Timer timer = new Timer(new TimerCallback((state) => { doSomething(); }));
timer.Change((int)TimeSpan.FromSeconds(30), (int)TimeSpan.FromSeconds(30));
return;
并且
Timer timer = new Timer(new TimerCallback((state) => { doSomething(); }));
timer.Change((int)TimeSpan.FromSeconds(30), Timeout.Infinite);
return;
答案 0 :(得分:6)
TimerCallback
引用了方法DoSomething()
,因此(在您的示例中)引用了this
,但没有其他方式的实时引用,因此应该收集它。 ..eventually
答案 1 :(得分:3)
计时器可能执行也可能不执行,取决于垃圾收集是否在执行时间之前运行。这就是为什么在堆栈以外的地方保留对定时器的引用是好的做法。
请注意,这并不总是有问题的;例如,只要它们仍在运行,就不会收集线程。
答案 2 :(得分:3)
这是一个快速测试:
class Program
{
static void Main(string[] args)
{
Something something = new Something();
Foo(something);
Console.ReadKey(true);
GC.Collect();
Console.ReadKey(true);
}
private static void Foo(Something something)
{
Timer timer = new Timer(new TimerCallback(something.DoIt),null,0,5);
return;
}
}
public class Something
{
public void DoIt(object state)
{
Console.WriteLine("foo{0}", DateTime.Now.Ticks);
}
}
这基本上就是编译器将其搞砸的(示例中的Lambda表达式)。当你运行它时,你会注意到只要你没有按下第一个键,它就会继续把东西放到控制台上。一旦你敲了一把钥匙,然后GC就会启动,它就会停止。 Timer
仍然引用了Something
,但没有任何引用Timer,所以它已经消失了。
答案 3 :(得分:0)
如果您正在讨论System.Threading.Timer
,它会实现IDisposable
,因此您应该保留对它的引用,以便在您不再使用它时可以调用Dispose。我不知道您的特定问题的答案,但您可以通过运行多次迭代并强制GC.Collect()
查看Timer是否继续触发来在控制台应用程序中进行调查。我的猜测是它最终将被收集并停止触发,除非在内部创建了一些静态的根参考。
另外,如果你想要一次性的Fire-and-forget计时器,你可以通过创建一个引用Timer的状态对象来实现一个,这样它就可以在计时器事件触发时自行处理。我有一个TimerService
类,其WhenElapsed(TimeSpan, Action)
方法使用此模式,它非常便于创建超时,而无需将Timer实例作为包含类中的字段进行管理。