Wir hatten in einem Projekt die Anforderung, dass alle X-Minuten oder Sekunden ein bestimmte SQL Abfrage ausgeführt wird und je nach Ergebnis die Daten verarbeitet werden. Dies kann man etwas "dirty” mit einer while(true) Schleife und Thread.Sleep bauen, oder man nutzt einen Timer.
Beispiel Szenario
Als kleines Beispiel wollen wir eine Konsolenanwendung bauen, die aller 10 Sekunden auf der Kommandozeile was ausgibt.
"Dirty” Variante
while (true) { Thread.Sleep(1000); Console.WriteLine("Bla!"); }
Diese Variante geht, ist aber nicht besonders edel. Und besonders wenn man zwei oder drei Sachen nach einer bestimmten Zeit machen will, kommt man wahrscheinlich nicht weit.
Timer im .NET Framework
Genau für solche Zwecke gibt es im .NET Framework mehrere Timer Klassen. Ein etwas älterer MSDN Artikel beschreibt 3 Vertreter:
Am Ende des Artikels findet sich ein guter Vergleich:
Der "System.Windows.Forms.Timer” kommt für mich eigentlich nur in Windows.Forms Anwendungen in Frage.
Interessant ist der Unterschied zwischen "System.Timers.Timer” und "System.Threading.Timer”.
Der Timer im "Threading” Namespace ist ohne weiteres zutun nicht thread safe. System.Timers.Timer baut intern auf den System.Threading.Timer auf. Für einfache Sachen ist der System.Timers.Timer sehr einfach. Hier der Link zu einer kleinen Stackoverflow-Diskussion.
Variante mit System.Timers.Timer
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Timers; namespace Timers.ConsoleApp { class Program { static void Main(string[] args) { Timer timer = new Timer(); timer.Interval = 1000; timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); timer.Enabled = true; Console.ReadLine(); } static void timer_Elapsed(object sender, ElapsedEventArgs e) { Console.WriteLine("Elapsed!"); } } }
Je nach Interval wird das entsprechende Event aufgerufen. Es wird ein Thread aus dem Threadpool genommen.
Noch eine andere Herangehensweise: AutoResetEvent
Mir bis vor kurzem ein total unbekannte Klasse: AutoResetEvent
AutoResetEvent _isStopping = new AutoResetEvent(false); TimeSpan waitInterval = TimeSpan.FromMilliseconds(1000); for (; !_isStopping.WaitOne(waitInterval); ) { Console.WriteLine("Bla!"); }
Im Grunde sieht es so aus wie die while(true) Geschichte. Funktioniert wahrscheinlich auch ähnlich ;)
Weitere Varianten werden in dieser Stackoverflow-Diskussion vorgestellt.
Timer in ASP.NET Applikationen
In meinem Beispiel gehe ich von einer Konsolenapplikation aus, aber könnte man sowas auch in einer ASP.NET Applikation machen? Jein.
Stackoverflow selbst soll wohl über einen kleinen Trick so einen "Background-Job” implementiert haben: Easy Background Tasks in ASP.NET
Der Trick besteht darin ein Item für eine bestimmte Dauer in den Cache zu legen. Läuft die Zeit ab, wird ein Event geworfen. In diesem Event wird die zeitgesteuerte Aktion ausgelöst und man hinterlegt wieder ein neues Item im Cache - und so weiter...
Das Problem: Im Normalfall fährt sich irgendwann der IIS Prozess runter und dann kann man da keine Aktionen mehr damit machen.
Viele Variante, was macht man nun?
Am einfachsten kommt mir der System.Timers.Timer vor. In ASP.NET Applikationen würde ich Abstand von zeitgesteuerten Sachen nehmen, da man nicht genau kalkulieren kann, wann der AppPool sich runterfährt. Besser einen Windows Service nehmen und dort über den Timer gehen - außer es geht nicht anders und man bastelt mit den anderen Varianten ;)
Wahrscheinlich...
... gibt es noch weit mehr Varianten. Wie macht ihr irgendwelche zeitgesteuerten Jobs? Gut wäre natürlich eine skalierbare Lösung. Man kann das natürlich auch per Scheduled Task und einem Konsolenprogramm lösen, aber elegant ist das ja auch nicht ;)
Wo meine Gedanken gerade Richtung Multithreading abschweifen: Richtig kompliziert wird es ja in Desktop-Apps bei multithreaded Geschichten wieder in den UI Thread zu schreiben - das ist aber ein anderes Thema :)
Also, wie macht ihr das? :)