2016-07-07 13 views
0

Ich habe eine Anwendung, die ich eine bestimmte Zeit warten muss, aber ich muss auch in der Lage sein, den aktuellen Vorgang bei Bedarf abzubrechen. Ich habe den folgenden Code:C# - Wie wird die Anwendung angehalten, bis der Timer beendet ist?

private void waitTimer(int days) 
{ 
    TimeSpan waitTime = TimeSpan.FromDays(days); 
    System.Timers.Timer timer = new System.Timers.Timer(waitTime.TotalMilliseconds); // Wait for some number of milliseconds 
    timer.Enabled = true; 
    timer.Start(); 
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler 

    while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested); // Loop forever untill timer is finished or operation is cancled. 

    timer.Elapsed -= new ElapsedEventHandler(OnTimedEvent); // Unsubscribe 

    DoWork(); // Do work when timer finishes....... 
} 

Unten ist der Ereignishandler für die Event-Timer beendet:

private void OnTimedEvent(object obj, ElapsedEventArgs e) 
{ 
    TimerSettings.TimerFinished = true; 
} 

Die while-Schleife nur Schleifen unendlich, bis der Timer beendet ist oder bis eine Abbruch-Anforderung wird gestellt in Ich möchte diese Funktionalität behalten, aber ich möchte lieber nicht für immer in die Schleife gehen, während ich darauf warte, dass der Timer beendet wird. Mein Timer kann so eingestellt werden, dass er in einem Intervall von mehreren Tagen läuft, so dass es nicht sinnvoll ist, so lange zu loopen.

Gibt es eine andere Möglichkeit, dies zu tun?

Ich weiß, was ich tun konnte:

Thread.Sleep(runDuration.TotalMilliseconds); 

Dies würde jedoch blockiert und ich wäre nicht in der Lage in einem Abbruch Anfrage gestellt.

EDIT: Also, um zu erläutern, was/warum ich hier pausieren muss, ist eine detailliertere Erklärung meiner Anwendung. Grundsätzlich möchte ich eine Anwendung haben, die in regelmäßigen Abständen "arbeitet". So basiert auf einer der Antworten unten zur Verfügung gestellt, wenn ich so etwas wie dies tat:

class Program 
{ 
    // Do something in this method forever on a regular interval 
    //(could be every 5min or maybe every 5days, it's up to the user) 
    static void Main(string[] args) 
    { 
     while(true) 
     { 
      if(args?.Length > 0) 
       waitTimer(args[0]); 
      else 
       wiatTimer(TimeSpan.FromDays(1).TotalSeconds); // Default to one day interval 
     }    
    } 

private void waitTimer(int numIntervals) 
{ 
    this.ElapsedIntervals = 0; 
    this.IntervalsRequired = numIntervals; 
    this.timer = new System.Timers.Timer(1000); // raise the elapsed event every second 
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler 
    //timer.Enabled = true; timer.Start() does this for you, don't do this 
    timer.Start(); 
    //thats all here 
} 

private void OnTimedEvent(object obj, ElapsedEventArgs e) 
{ 
    this.ElapsedIntervals += 1; 
    if(this.CancelRequested) 
    { 
     this.ElapsedIntervals = 0; 
     this.timer.Stop(); 
     return; 
    } 
    if(this.ElapsedIntervals >= this.IntervalsRequired) 
    { 
     this.ElapsedIntervals = 0; 
     this.timer.Stop(); 
     DoWork(); // This is where my work gets done....... 
     return; 
    } 
    } 
} 

Dann ist mein Service/console app würde in eine Endlosschleife beginnen und gehen, die gerade Timer den ganzen Tag lang fest. Zuvor war Anhalte ich tatsächlich die Ausführung irgendeinen anderen Code an:

while (!TimerSettings.TimerFinished && !quitToken.IsCancellationRequested); 

, die mindestens gearbeitet, aber wie bereits erwähnt, kann möglicherweise intensive Art und Weise seine Ressource einen Thread zu unterbrechen. Was ich wirklich brauche, ist eine Möglichkeit, meinen Thread zu blockieren, bis der Timer abgelaufen ist.

EDIT2: Dies ist meine letzte Implementierung, die für mich mit einem Warte Griff ...

class TimerClass 
{ 
    /// <summary> 
    /// Initialize new timer. To set timer duration, 
    /// either set the "IntervalMinutes" app config 
    /// parameter, or pass in the duration timespan. 
    /// </summary> 
    /// <param name="time"></param> 
    internal bool StartTimer(CancellationToken quitToken, TimeSpan? duration = null) 
    { 
     TimeSpan runDuration = new TimeSpan(); 
     runDuration = duration == null ? GetTimerSpan() : default(TimeSpan); 

     if (runDuration != default(TimeSpan)) 
     { 
      WaitTimer(runDuration); // Waits for the runduration to pass 
     } 
     return true; 
    } 

    /// <summary> 
    /// Get duration to run the timer for. 
    /// </summary> 
    internal TimeSpan GetTimerSpan() 
    { 
     TimerSettings.Mode = App.Settings.Mode; 
     DateTime scheduledTime = new DateTime(); 

     switch (TimerSettings.Mode) 
     { 
      case "Daily": 
       scheduledTime = DateTime.ParseExact(App.Settings.ScheduledTime, "HH:mm:ss", CultureInfo.InvariantCulture); 
       if (scheduledTime > DateTime.Now) 
        TimerSettings.TimerInterval = scheduledTime - DateTime.Now; 
       else 
        TimerSettings.TimerInterval = (scheduledTime + TimeSpan.FromDays(1)) - DateTime.Now; 
       break; 
      case "Interval": 
       double IntervalMin = double.TryParse(App.Settings.PollingIntervalMinutes, out IntervalMin) ? IntervalMin : 15.00; 
       int IntervalSec = Convert.ToInt32(Math.Round(60 * IntervalMin)); 
       TimeSpan RunInterval = new TimeSpan(0, 0, IntervalSec); 
       TimerSettings.TimerInterval = RunInterval; 
       break; 
      case "Manual": 
       TimerSettings.TimerInterval = TimeSpan.FromMilliseconds(0); 
       break; 
      default: 
       TimerSettings.TimerInterval = (DateTime.Today + TimeSpan.FromDays(1)) - DateTime.Now; 
       break; 
     } 
     return TimerSettings.TimerInterval; 
    } 

    /// <summary> 
    /// Event handler for each timer tick. 
    /// </summary> 
    /// <param name="obj"></param> 
    /// <param name="e"></param> 
    private void OnTimedEvent(object obj, ElapsedEventArgs e) 
    { 
     ElapsedIntervals += 1; 
     if (CancelRequested.IsCancellationRequested) // If the application was cancled 
     { 
      ElapsedIntervals = 0; 
      timer.Stop(); 
      WaitHandle.Set(); 
      return; 
     } 
     if (ElapsedIntervals >= IntervalsRequired) // If time is up 
     { 
      ElapsedIntervals = 0; 
      timer.Stop(); 
      WaitHandle.Set(); 
      return; 
     } 
    } 

    /// <summary> 
    /// Timer method to wait for a 
    /// specified duration to pass. 
    /// </summary> 
    /// <param name="span"></param> 
    private void WaitTimer(TimeSpan span) 
    { 
     WaitHandle = new AutoResetEvent(false); 
     int tickDuration = 1000; // Number of milliseconds for each tick 
     IntervalsRequired = Convert.ToInt64(span.TotalMilliseconds/(tickDuration > 0 ? tickDuration : 0.01)); 
     timer = new System.Timers.Timer(tickDuration);   // Raise the elapsed event every tick 
     timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler for when each tick is complete 
     timer.Start();   // Start ticking 
     WaitHandle.WaitOne(); // Halt the main thread untill span is reached 
    } 


    // Timer parameters: 
    private static long ElapsedIntervals { get; set; } 
    private static long IntervalsRequired { get; set; } 
    private static System.Timers.Timer timer { get; set; } 
    private static CancellationToken CancelRequested { get; set; } 
    private static string Mode { get; set; } 
    private static TimeSpan TimerInterval { get; set; } 
    private static EventWaitHandle WaitHandle { get; set; } 
} 

internal static class TimerSettings 
{ 
    internal static string Mode { get; set; } 
    internal static TimeSpan TimerInterval { get; set; } 
} 
+0

Sie sollten näher darauf eingehen, was Sie "pausieren" wollen und warum. – Jakotheshadows

+0

Wie in einer Antwort erwähnt, ist eine Busy-Schleife _awful_. Vielleicht würde es mit einem 'Thread.Sleep()' langsamer machen, aber nur ein bisschen und es ist einfach nicht nötig. Sie können es _right_ ohne jede Schwierigkeit tun. Siehe das markierte Duplikat. Sie können entweder 'Wait()' wie in der akzeptierten Antwort gezeigt oder (noch besser) eine 'async' Methode verwenden und' 'task'' erwarten. –

Antwort

1

Sie sollten sich die Dokumentation zum Timer.Elapsed Event ansehen. Dieses Ereignis wird jedes Mal wiederholt, wenn das Intervall abläuft, während die AutoReset-Eigenschaft auf "True" festgelegt ist (Standardeinstellung).Ich würde Ihre eigene Anzahl der verstrichenen Intervalle beibehalten und diese mit den erforderlichen verstrichenen Intervallen in diesem Ereignishandler vergleichen, um zu überprüfen, ob es Zeit ist, den Timer zu stoppen. In diesem Fall können Sie auch Stornierungen vornehmen. Wenn Ihr Timer die erforderliche Anzahl von Intervallen beendet, können Sie Ihre DoWork-Funktion von diesem Ereignishandler aufrufen.

private void waitTimer(int numIntervals) 
{ 
    this.ElapsedIntervals = 0; 
    this.IntervalsRequired = numIntervals; 
    this.timer = new System.Timers.Timer(1000); // raise the elapsed event every second 
    timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); // Subscribe to event handler 
    //timer.Enabled = true; timer.Start() does this for you, don't do this 
    timer.Start(); 
    //thats all here 
} 

private void OnTimedEvent(object obj, ElapsedEventArgs e) 
{ 
    this.ElapsedIntervals += 1; 
    if(this.CancelRequested) 
    { 
     this.ElapsedIntervals = 0; 
     this.timer.Stop(); 
     return; 
    } 
    if(this.ElapsedIntervals >= this.IntervalsRequired) 
    { 
     this.ElapsedIntervals = 0; 
     this.timer.Stop(); 
     DoWork(); 
     return; 
    } 
} 

https://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed(v=vs.110).aspx

Wie ich es sehe, in Bezug auf „Pausieren“, gibt es zwei Gründe pausieren zu wollen, und ich bin nicht sicher, welche Grund liegt bei Ihnen:

  1. Sie wollen verhindern, dass die Anwendung die Ausführung "beendet" und normal beendet wird.
  2. Sie möchten die Ausführung anderer Code warten, bis die Anzahl der erforderlichen Intervalle

abgelaufen ist Wenn Ihr Grund ist, # 2, dann ist diese Antwort vollständig ist.

+0

Dies ist ein Windows-Dienst. Ich mag Ihre Timer-Implementierung, aber meine Anwendung pausiert nicht. –

+0

Was ist also der Grund, dass Sie die Anwendung "pausieren" müssen? Wäre es nicht in Ordnung, nur Ihre letzte Codezeile in der relevanten Funktion den Aufruf von WaitTimer zu machen und dann den Rest der Arbeit zu beenden, indem Sie eine entsprechende DoWork-Funktion aus dem Ereignishandler Elapsed aufrufen? – Jakotheshadows

+0

Ich sehe Ihren Punkt, aber basierend auf dem Fluss meiner Anwendung, nach der "timer.Start();" - Zeile in der WaitTimer-Methode, wird die Anwendung einfach die Ausführung fortsetzen. Sagen wir, ich habe einen Prozess, der 10 Mal in einer Schleife läuft. In der ersten Schleife rufe ich 'waitTimer (10)' auf. Mein erwartetes Verhalten wäre zehn Sekunden warten dann DoWork(). Was tatsächlich passiert, ist waitTimer() wird 10 Mal hintereinander hintereinander aufgerufen und doWork() passiert 10 Mal auf einmal nach den ersten 10 Sekunden! Ich würde erwarten, dass DoWork einmal alle 10 Sekunden passiert. –

-2

Zunächst einmal scheint zu funktionieren: (!)   „Sie absolut nicht wollen‚busy -warte 'für alles! " (BAD Hund! NO Biscuit!)

Ähem ...

Eine praktische Lösung für dieses Problem warten timed ein auszuführen ist auf einer Semaphore (oder einem anderen geeigneten gegenseitigen Ausschlussobjekt. ..), anstatt einen tatsächlichen Timer zu verwenden. Wenn Sie das Warten beenden müssen, bevor es fertig ist, streichen Sie einfach das, worauf es wartet.

Das kritische Problem mit Ihrer derzeitigen "Lösung" ist, dass es den Prozess in 100% CPU-Auslastung, ganz verschwenderisch knallen wird. Nie mach das!

+2

"Das kritische Problem mit Ihrer gegenwärtigen" Lösung "ist, dass es den Prozess in 100% CPU" knallt ", ja, aber ein' Thread.Sleep (100) 'oder passender Wert in seiner while-Schleife wird das beheben (anstatt sich zu drehen) mit voller Geschwindigkeit). Es wäre auch schön, wenn Sie Ihre "praktische Lösung" sehen würden, denn obwohl ich Ihre Antwort gelesen habe, habe ich keine Ahnung, wie ich sie implementieren soll und würde stattdessen einfach einen "Sleep" in den OP-Code einfügen. – Quantic

+0

Bewerten Sie Ihren Kommentar, denn in der realen Welt könnte das sehr gut sein. "Was auch immer funktioniert, * 'gut genug ** für Friedensarbeit", wie mein verstorbener Onkel zu sagen pflegte. Ein "100-Millisekunden-Schlaf" innerhalb der ansonsten ausgelasteten Warteschleife würde in der Tat das Problem umgehen, und ich bin * total * für "was auch immer funktioniert". –

+0

Ich stimme dem Kommentar zu, dass diese Antwort ohne ein Codebeispiel nicht sehr nützlich ist. Ehrlich gesagt, die Frage wurde schon oft gestellt, also glaube ich nicht, dass wir noch eine weitere Antwort auf Stack Overflow benötigen. Aber wenn Sie einen anbieten, sollte es ein guter sein. Ein zeitgesteuertes Warten auf ein SemaphoreSlim, ein ManualResetEvent usw. ist kein schlechter Vorschlag, obwohl die aktuellen TPL-Funktionen etwas veraltet sind. –