2012-01-18 13 views
12

Piszę usługę Windows, która będzie przetwarzać "coś" co kilka minut.Zachowanie timera, gdy wykonanie trwa dłużej niż zakres?

Oto kod:

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(); 
      } 
     } 

Zastanawiam się, co się stanie, jeśli this.processor.Run() zajmie dużo czasu i następny TimerElapsed zdarzenie zostanie podniesiona? Czy to pominie? Czy po zakończeniu będzie czekać i działać jak najszybciej? Czy powinienem rozważyć te scenariusze i kod dla nich?

Używam System.Timers.Timer

EDIT:

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(); 
      } 
     } 

EDIT 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(); 
      } 
     } 

Odpowiedz

10

Będzie to nazwać ponownie w innym wątku.

W zależności od charakteru operacji będzie chciał albo:

  1. ignorować tego, czy kod nazywany jest bezpieczna dla wielu jednoczesnych połączeń, może to być w porządku. Oczywiście, musisz wiedzieć,, że jest w porządku.
  2. Zablokuj działanie wyzwalane przy użyciu zegara. Pamiętaj, że możesz skończyć z kolejką wielu oczekujących operacji, co jest bardzo złe.
  3. Zablokuj operację wyzwalaną za pomocą timera, spróbuj uzyskać blokadę z limitem czasu zero, a jeśli ci się nie uda, pomiń to - od ostatniej chwili jest tu wątek.
  4. Mierz timer jako jednorazowy timer, który uruchamiasz ponownie po zakończeniu każdego połączenia.
+0

Lubię # 4 w mojej konkretnej sytuacji. Czy moja edycja (w oryginalnym wpisie) wygląda poprawnie, jeśli chcę tego scenariusza? – katit

+4

Zatrzymanie licznika czasu nie pomoże, przedłużenie sesji obsługi zdarzenia może być opóźnione, jeśli w procesie jest dużo aktywnych wątków TP. Ustaw właściwość AutoReset na wartość false. –

+0

Byłbym bardzo ostrożny z 2. 1 albo wyraźnie działa, albo nie, ale jest wspaniały, kiedy naprawdę potrzebujesz go co X minut. Różnica między 3 a 4 wynosi pomiędzy "co x minut z kilkoma pominiętymi" i "co x minut po ostatnim zakończeniu". Oba mają swoje miejsce i często albo wszystko jest w porządku. Nie musisz '.Stop()' zegara, jest to łatwiejsze i bezpieczniejsze (w niektórych przypadkach niektóre dziwaczne przypadki oznaczają, że nawet nie posunąłeś się tak daleko), aby nie był to automatyczny reset. Ustaw "AutoReset" na false lub użyj System.Threading.Timer z okresem 'Timeout.Infinite'. –

3

Można zobaczyć, co stanie się z tej próbki app:

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

    } 
} 

Zobaczysz dwa „wszedł” struny pokazać się przed „wyszedł” pierwszy zjawia. Będzie kontynuowany. Więc nici nie będą się nawzajem na siebie nakładać.

(BTW, ja nie opowiadając nieskończone pętle. :))

+0

Nie ma powodu, aby nie popierać nieskończonej pętli. Nieliczne przypadki, w których umyślne miejsce ma swoje miejsce (np. Pompa komunikatów okiennych) to nieliczne przypadki, w których umyślne mają swoją sprawę, a reszta to wyraźnie błędy, a nie w "może wydawać się dobrym pomysłem, więc nie adwokat to "obóz. –

2

Kiedy zdarzenie timera jest podniesiona, kod czasowy jest planowane do realizacji na puli wątków. Najprawdopodobniej zostanie wykonany w innym wątku, ale zależy od różnych czynników (liczba procesorów, wykorzystanie wątków itp.). Jednak nie ma nic z zegarami - jest to obowiązek wątku.

Osobiście nigdy nie używam interwału licznika czasu. Ustawiam timer na uruchamianie raz i po wykonaniu mojego kodu, ustaw go ponownie. Zapewniam więc, że kod jest wykonywany tylko w jednym wątku.

3

używam następujących jeśli nie chcę kolejnych Firings czasomierza wykonać metodę ponownie zanim zostanie zakończona:

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

inaczej, jeśli metoda może mieć okres dłuższy niż przedziału czasowego, metoda może zakończyć uruchamianie w innym wątku przed zakończeniem pierwszego.

+0

W twoim przypadku, jeśli czas trwania metody jest dłuższy niż przedział czasowy, otrzymasz wiele oczek i metoda będzie wykonywana bez przerwy. –

+0

@the_joric - Nie sądzę, że to prawda. TryEnter nie jest metodą blokującą i zwraca natychmiast, zwracając wartość true lub false. Jeśli uzyska blokadę, wykonuje pracę wewnątrz. Jeśli tak się nie stanie, metoda TimerFired zostanie szybko zakończona bez wykonywania żadnej pracy. – hatchet

+0

tak, masz rację - moja wina :) –

0

Zrobiłem to, aby poradzić sobie z takimi scenariuszami. Off-course należy dostosować go do wyjątkowych przypadków, takich jak działania wielowątkowe.

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; 
     } 
    } 
0

Zachowanie timera, gdy wykonanie trwa dłużej niż zakres?

Gdy czas wykonania zadania trwa dłużej niż przedział czasu timera. TimerElapsed Event zostanie podniesiony do nowego wątku. Nie zostanie pominięty. Multi Threading wprowadzony do System.Timers.Timer

Powiązane problemy