Контекст базы данных EF блокирует все System.Threading.Timers, когда обратный вызов является статическим - PullRequest
0 голосов
/ 25 марта 2012

Когда TimerCallback статичен, он будет выполняться только один раз и просто зависнет и даже заблокирует любые другие таймеры, такие как timer2.Это достигает конца метода, и никакие исключения не выбрасываются.Если я сделаю обратный вызов нестатичным, то он будет работать, как и ожидалось, вызывая метод каждые x секунд.Если я удаляю контекст базы данных, то он работает статически или нет.System.Threading.Timer должен ставить в очередь вызовы рабочего потока, поэтому даже если БД блокирует поток, я не вижу, как он может блокировать ВСЕ потоки, даже те, которые не обращаются к базе данных.Кто-нибудь может объяснить, почему он так себя ведет?

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...");
    }
}

Именно здесь запускается таймер.После запуска основной поток продолжит нормально, но таймеры все еще зависают.

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 Ответ

3 голосов
/ 26 марта 2012

Затем он вызывает ReadLine (), так что myTimer никогда не выходит за рамки видимости, так как же их можно собирать?

Нет, это не так, как работает сборщик мусора. myTimer может быть собран сразу после вызова Start (), так как больше нет ссылок на объект. Если вы хотите сохранить myTimer до конца метода, вам нужно добавить GC.KeepAlive (myTimer) в конце метода.

Это специфическая проблема с таймерами, в частности System.Threading.Timer. Сборщик мусора должен видеть живую ссылку на объект, чтобы предотвратить его сборку мусора. В вашем коде нет такой ссылки, локальная переменная не живет достаточно долго и обратный вызов является статическим.

Среда CLR прилагает некоторые усилия, чтобы поддерживать System.Timers.Timer, он не будет собираться, пока он включен. Но как только он отключен, он получает право на сбор. Что, конечно, хорошо, если нет ссылки, то вы больше не сможете ее включить. System.Threading.Timer не имеет такого же механизма, вы должны сохранять ссылки на него сами, чтобы поддерживать его. Что является одной из основных причин того, что существуют два класса таймеров, которые, по-видимому, выполняют ту же работу. У них нет, System.Timers.Timer имеет свои собственные прелести.

...