Синхронизация метода Timers.Timer при остановке - PullRequest
6 голосов
/ 27 января 2011

Со ссылкой на эту цитату из MSDN о System.Timers.Timer:

Событие Timer.Elapsed возникает на Тема ThreadPool, так что метод обработки событий может выполняться на одном в то же время, что вызов метод Timer.Stop работает на другом нить. Это может привести к Истекшее событие поднимается после Метод Stop вызывается. Эта гонка состояние не может быть предотвращено просто сравнивая свойство SignalTime со временем, когда метод Stop называется, потому что обработка событий метод может уже выполняться, когда метод Stop вызывается или может начать выполнение между моментом когда вызывается метод Stop и момент, когда время остановки сохранено. Если важно предотвратить поток который вызывает метод Stop из продолжая в то время как обработка событий метод все еще выполняется, используйте более надежный механизм синхронизации такой в качестве класса монитора или Метод CompareExchange. Код, который использует метод CompareExchange может быть найдено в примере для Timer.Stop метод.

Кто-нибудь может привести пример " надежного механизма синхронизации, такого как класс Monitor ", чтобы объяснить, что именно это означает?

Я думаю, это означает, что нужно каким-то образом использовать блокировку, но я не уверен, как бы вы это реализовали.

Ответы [ 5 ]

12 голосов
/ 27 января 2011

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

Вам потребуются две синхронизации: одна для обеспечения остановки таймера только тогда, когда не запущен обработчик событий Elapsed, другая для гарантии того, что эти резервные потоки TP не причинят вреда. Как это:

    System.Timers.Timer timer = new System.Timers.Timer();
    object locker = new object();
    ManualResetEvent timerDead = new ManualResetEvent(false);

    private void Timer_Elapsed(object sender, ElapsedEventArgs e) {
        lock (locker) {
            if (timerDead.WaitOne(0)) return;
            // etc...
        }
    }

    private void StopTimer() {
        lock (locker) {
            timerDead.Set();
            timer.Stop();
        }
    }

Рекомендуется установить для свойства AutoReset значение false. С другой стороны, это хрупко, событие Elapsed вызывается из внутреннего метода .NET, который перехватывает Exception. Очень неприятно, ваш таймер перестает работать без какой-либо диагностики. Я не знаю истории, но в MSFT должна была быть другая команда, которая пыхтела и пыхтела в этом беспорядке и писала System.Threading.Timer. Настоятельно рекомендуется.

0 голосов
/ 27 января 2011

Кажется, таймер не безопасен для потоков. Вы должны синхронизировать все вызовы с помощью блокировки. lock (object) {} на самом деле является просто сокращением для простого вызова монитора.

0 голосов
/ 27 января 2011

Попробуйте:

lock(timer) {
timer.Stop();
}
0 голосов
/ 27 января 2011

Вот очень простой способ предотвратить возникновение этого состояния гонки:

private object _lock = new object();
private Timer _timer; // init somewhere else

public void StopTheTimer()
{
    lock (_lock) 
    {
        _timer.Stop();
    }
}

void elapsed(...)
{
    lock (_lock)
    {
        if (_timer.Enabled) // prevent event after Stop() is called
        {
            // do whatever you do in the timer event
        }
    }
}
0 голосов
/ 27 января 2011

Это то, что он предлагает.

Monitor - это класс, который используется компилятором C # для оператора lock.

При этомВышеуказанное является проблемой только в том случае, если это проблема в вашей ситуации.Весь оператор в основном переводится как «Вы можете получить событие таймера, которое происходит сразу после вызова Stop (). Если это проблема, вам нужно с ней справиться».В зависимости от того, что делает ваш таймер, это может быть проблемой, а может и не быть.

Если это проблема, страница Timer.Stop показывает надежный способ (использование Interlocked.CompareExchange ) чтобы справиться с этим.Просто скопируйте код из образца и при необходимости измените его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...