当回调是静态时,EF数据库上下文会阻塞所有System.Threading.Timers

时间:2012-03-25 19:28:27

标签: c# multithreading entity-framework static timer

当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
}

1 个答案:

答案 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。