Поведение таймера, когда выполнение занимает больше времени, чем span? - PullRequest
17 голосов
/ 19 января 2012

Я пишу службу Windows, которая будет обрабатывать «что-то» каждые пару минут.

Вот код:

public Service()
        {
            this.InitializeComponent();
            this.ServiceName = Name;
            this.CanPauseAndContinue = true;
            this.CanShutdown = true;

            this.eventLog.Source = Name;

            // initialize timer
            this.timer.Elapsed += this.TimerElapsed;
        }

        private void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            eventLog.WriteEntry("Starting syncronization...", EventLogEntryType.Information);

            if (this.processor.PrepareToRun())
            {
                this.processor.Run();
            }
        }

Интересно, что произойдет, если this.processor.Run() займетдолго и следующее событие TimerElapsed будет возбуждено?Будет ли это пропустить?Будет ли он ждать и запустить как можно скорее после завершения?Должен ли я рассмотреть эти сценарии и код для них?

Я использую System.Timers.Timer

РЕДАКТИРОВАТЬ :

private void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            eventLog.WriteEntry("Starting syncronization...", EventLogEntryType.Information);

            try
            {
                this.timer.Stop();
                if (this.processor.PrepareToRun())
                {
                    this.processor.Run();
                }
            }
            catch (Exception ex)
            {
                LoggingAndNotifications.LogAndNotify(ex);

            }
            finally
            {
                this.timer.Start();
            }
        }

РЕДАКТИРОВАТЬ2

public Service()
        {
            this.InitializeComponent();
            this.ServiceName = Name;
            this.CanPauseAndContinue = true;
            this.CanShutdown = true;

            this.eventLog.Source = Name;

            // initialize timer
            this.timer.AutoReset = false;
            this.timer.Elapsed += this.TimerElapsed;
        }

        private void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            eventLog.WriteEntry("Starting syncronization...", EventLogEntryType.Information);

            try
            {
                if (this.processor.PrepareToRun())
                {
                    this.processor.Run();
                }
            }
            catch (Exception ex)
            {
                LoggingAndNotifications.LogAndNotify(ex);
                throw;
            }
            finally
            {
                this.timer.Start();
            }
        }

Ответы [ 6 ]

14 голосов
/ 19 января 2012

Это вызовет его снова в другом потоке.

В зависимости от характера операции вы можете либо:

  1. Игнорируйте это, если вызываемый код безопасен для нескольких одновременных вызовов, тогда это может быть хорошо. Конечно, вы должны знать , что все в порядке.
  2. Блокировка при срабатывании по таймеру. Имейте в виду, что вы можете получить очередь из множества ожидающих операций, что очень плохо.
  3. Блокировка при срабатывании по таймеру, попытайтесь получить блокировку с таймаутом, равным нулю, и если вы не справитесь, пропустите ее - с прошлого раза здесь еще есть поток.
  4. Имейте таймер в качестве одноразового таймера, который вы перезапускаете в конце каждого вызова.
4 голосов
/ 19 января 2012

Я использую следующее, если не хочу, чтобы последующие срабатывания таймера снова выполняли метод до его завершения:

private void TimerFired(object sender, System.Timers.ElapsedEventArgs e) {
    // only execute the code within this method if we are able to
    // get a lock. This will ensure that any Timer firings will be
    // ignored that occur while we're already doing work (OnTimer) 
    if (Monitor.TryEnter(lockObj)) {
        try {
            // do work here
        } finally {
            Monitor.Exit(lockObj);
        }
    }
}

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

3 голосов
/ 19 января 2012

Вы можете увидеть, что будет с этим примером приложения:

class Program
{
    static void Main(string[] args)
    {
        System.Timers.Timer timer = new System.Timers.Timer(2000);
        timer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedObject);
        timer.Start();

        while (true)
        {
        }

    }

    static void OnTimedObject(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("entered");
        Thread.Sleep(3000);
        Console.WriteLine("exited");

    }
}

Вы увидите две «введенные» строки, прежде чем появится «выходная». Это будет продолжаться. Так что нити не наступят друг на друга.

(Кстати, я не защищаю бесконечные циклы. :))

2 голосов
/ 19 января 2012

Когда происходит событие таймера, код таймера планируется для выполнения в пуле потоков. Скорее всего, он будет выполнен в другом потоке, но это зависит от разных факторов (число процессоров, использование потоков и т. Д.). Однако это не имеет ничего общего с таймерами - это обязанность пула потоков.

Лично я никогда не использую интервал таймера. Я настроил таймер для запуска один раз, и после того, как мой код будет выполнен, настройте его снова. Таким образом, я гарантирую, что код выполняется только в одном потоке.

0 голосов
/ 14 июля 2017

Поведение таймера, когда выполнение занимает больше времени?

Когда время выполнения задачи занимает больше времени, чем Таймер.Событие TimerElapsed будет создано в новом потоке.Это будет не пропущено.Многопоточность, реализованная в System.Timers.Timer

0 голосов
/ 04 августа 2013

Я сделал это для обработки таких сценариев.Конечно, вам нужно настроить его для исключительных случаев, таких как многопоточные действия.

    public Service()
    {
        bool _IsProcessRunning = false;
        this.InitializeComponent();
        this.ServiceName = Name;
        this.CanPauseAndContinue = true;
        this.CanShutdown = true;

        this.eventLog.Source = Name;

        // initialize timer
        this.timer.Elapsed += this.TimerElapsed;
    }

    private void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        if(!_IsProcessRunning)
        {
            DoSomething();
        }           
    }

    private void DoSomething()
    {
        try
        {
             _IsProcessRunning = true;

             // Do our stuff here
        }
        catch(Exception Ex)
        {               
        }
        finally
        {
             _IsProcessRunning = false;
        }
    }
...