当TimerCallback是静态的时,它只会执行一次,只是挂起,甚至会阻止正在运行的任何其他定时器,如timer2。它确实到达方法的末尾并且没有抛出异常。如果我使回调非静态,那么它按预期工作每隔x秒调用一次方法。如果我删除数据库上下文然后它可以正常工作静态或不。 System.Threading.Timer应该在工作线程上排队调用,所以即使db阻塞了一个线程,我也看不到它如何阻止所有线程,甚至是那些没有访问数据库的线程。任何人都可以解释为什么它会像这样吗?
public class TimerClass
{
private System.Threading.Timer timer1;
private System.Threading.Timer timer2;
public void Start(TimeSpan CheckInterval)
{
//timer2 = new Timer(o => Console.WriteLine("Timer2: " + DateTime.Now.ToString()), null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
timer1 = new Timer(TimerCallback, null, TimeSpan.Zero, CheckInterval);
}
private static void TimerCallback(object state)
{
Console.WriteLine("Timer1: " + DateTime.Now.ToString());
using (MyDbEntities db = new MyDbEntities())
{
foreach (var item in db.Table)
Console.WriteLine(item.Name);
}
Console.WriteLine("Leaving Callback...");
}
}
这是Timer启动的地方。启动后,这个主线程将继续正常,但定时器仍然挂起。
static void Main()
{
TimerClass myTimer = new TimerClass();
myTimer.Start(TimeSpan.FromSeconds(1));
Console.WriteLine("Timer started...");
Console.ReadLine(); // pausing here, timers are still in-scope and should be firing
}
答案 0 :(得分:3)
然后它调用ReadLine(),所以myTimer永远不会超出范围,所以它们怎么可能被垃圾收集?
不,这不是垃圾收集器的工作原理。可以在Start()调用之后立即收集myTimer,因为没有对该对象的进一步引用。如果你想让myTimer保持活着直到方法结束,那么你必须在方法结束时添加GC.KeepAlive(myTimer)。
这是计时器的一个特定问题,尤其是System.Threading.Timer。垃圾收集器必须查看对象的实时引用,以防止它被垃圾收集。你的代码中没有这样的引用,局部变量的寿命不够长,回调是静态的。
CLR确实会努力保持System.Timers.Timer的运行,只要它已启用就不会收集它。但是一旦它被禁用,它就有资格收集。当然没关系,如果没有参考,那么你就不能再启用它了。 System.Threading.Timer没有相同的机制,您必须自己保持引用它以使其保持活动状态。这是两个 Timer类似乎完成相同工作的核心原因之一。他们没有,System.Timers.Timer有自己的cooties。