C# 清空System.Threading.Timer会停止它吗?
如果我有一个活动的,并将其设置为null,它是否停止 我意识到调用C# 清空System.Threading.Timer会停止它吗?,c#,C#,如果我有一个活动的,并将其设置为null,它是否停止 我意识到调用.Dispose()更合适,但我希望得到问题的书面答案 public class Foo { private System.Threading.Timer _timer; public Foo() { // initialize timer } public void KillTimer() { _timer=null; } } 更新: 在反复讨论将对Syst
.Dispose()
更合适,但我希望得到问题的书面答案
public class Foo
{
private System.Threading.Timer _timer;
public Foo()
{
// initialize timer
}
public void KillTimer()
{
_timer=null;
}
}
更新: 在反复讨论将对System.Threading.Timer的单个引用设置为null是否确实会导致停止后,我们发现
GC.Collect()
,然后调用回调
谢谢大家。不,它不会停止
将变量设置为null
不会直接产生任何副作用,例如停止计时器。(除非是财产)
由于计时器有其他引用,GC将不会收集它,并且它永远不会停止。为什么会停止
考虑:
System.Threading.Timer t = ...;
System.Threading.Timer q = t;
q = null; // Should this stop it as well?
设置为null是对变量执行的操作,而不是对对象执行的操作。计时器无法知道您将某个特定变量设置为null,因此无法在此基础上采取行动
编辑:
为了解决编辑问题,即使在只有一个引用的情况下,也不能保证计时器会停止,因为在引用被设置为null后GC可能不会运行。这也并非完全不可能,Microsoft.NET实现使用了分代收集器,静态字段很可能在托儿所集合中幸存下来,并被提升到老一代。如果您的程序具有相对稳定的内存配置文件,则可能永远不会有旧一代的集合(并且扩展来说,终结器在程序结束之前不会运行)。不一定。将它设置为null,将删除对它的任何引用,并依赖垃圾收集器来处理它
如果计时器在GC到达之前关闭,它将触发事件。我知道您在询问有关
System.Threading.timer
类的问题,但我想指出一些相当重要的问题
到目前为止提供的答案是好的。将任何变量设置为null
都不会直接影响变量先前分配到的对象,这是正确的。但是,当垃圾收集器最终处理计时器时,它将停止
SLaks表示在将计时器引用设置为null
后,可能存在延迟引用。在一个System.Threading.Timer
reference的简单示例中,情况并非如此
但是,例如,如果您有一个,并且您处理了它的已用事件
,那么将它设置为空
将留下一个引用,计时器将永远运行
请考虑这个代码,例如:
var t = new System.Timers.Timer(1000.0);
t.AutoReset = true;
t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
Console.Write("Press Enter to start the timer.");
Console.ReadLine();
t.Start();
Console.Write("Press Enter to set t to null.");
Console.ReadLine();
// This will not stop the timer. It actually does nothing at all to the timer
// to which t has been assigned.
t = null;
Console.Write("Press Enter again to perform a garbage collection.");
Console.ReadLine();
// This STILL will not stop the timer, as t was not the only reference to it
// (we created a new one when we added a handler to the Elapsed event).
GC.Collect();
Console.Write("t is null and garbage has been collected. Press Enter to quit.");
Console.ReadLine();
在上面的示例中,由于有代码保留对t
的引用以处理其已用事件,计时器将永远不会停止
再一次,我意识到这不是你问的那门课;我提出这一点只是想指出,事实上,对于给定对象是否有更多的引用并不总是显而易见的
更新:关于我上面的陈述是否同样适用于系统.Threading.Timer
对象,似乎出现了一些混乱它不是。为了验证这一点,考虑下面代码的修改:
Console.Write("Press Enter to start the timer.");
Console.ReadLine();
var t = new System.Threading.Timer(
state => { Console.WriteLine(DateTime.Now); },
null,
0,
1000
);
Console.Write("Press Enter to set t to null.");
Console.ReadLine();
// This will not stop the timer. It actually does nothing at all to the timer
// to which t has been assigned. HOWEVER, if/when the GC comes around to collect
// garbage, it will see that said timer has no active references; and so it will
// collect (and therefore finalize) it.
t = null;
Console.Write("Press Enter again to perform a garbage collection.");
Console.ReadLine();
// This WILL cause the timer to stop, as there is code in the type's
// finalizer to stop it.
GC.Collect();
Console.Write("t is null and garbage has been collected. Press Enter to quit.");
Console.ReadLine();
这就是为什么它不适用于System.Timers.Timer
(或任何实际具有事件的类型):
当我们像这样定义事件处理程序时,很容易忽略答案:
t.Elapsed += (sender, e) => Console.WriteLine(DateTime.Now);
如果我这样定义我的处理程序呢
t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());
哦,对了!那个从来没有人注意过的sender
论点
NET提供的事件处理基础结构要求处理事件的对象维护对引发事件的对象的引用。否则,EventHandler
代表及其所有表亲的签名所提供的合同将被违反
这个故事的寓意是:只要向事件添加处理程序,就创建了对对象的新引用。在这一点之后,允许对该对象进行垃圾收集的唯一方法是删除处理程序——但如果您将对该对象的唯一其他引用设置为null
(这是.NET程序可能存在“内存泄漏”的极少数示例之一),则这可能非常困难.GC会自动清理未引用的对象,因此将其设置为null“可能”会产生效果。@Russell如果收集器运行,并且这是对计时器的唯一引用。将变量设置为null和停止计时器之间没有直接的因果关系。因果关系是“如果计时器没有更多引用”,那么GC将处理它。“正确编写的程序不能依赖终结器的副作用。”@code:你说有事件,对吗?为空字段不会对事件列表中的委托实例产生任何影响,这些实例都包含对对象的引用。真的,答案是:不要那样做,它不会做你认为它会做的。嘿,slaks。我们在谈论什么其他参考资料?@kbrimington:工作示例不现实,因为在这个应用程序中,垃圾收集器可能永远不会运行。添加一个GC.Collect
行,然后您将看到计时器停止。因此,说“它永远不会停止”并不准确。它可能不会(事实上,几乎肯定不会)在设置为null
@kbrimington之后立即以任何确定的方式停止——您可能应该添加代码示例作为您自己的答案。我不知道这是真的
t.Elapsed += (sender, e) => Console.WriteLine(sender.GetType());