Es gibt nicht unbedingt einen "besten Weg", um Code für lange laufende Ereignisverarbeitung zu schreiben. Es hängt davon ab, welche Art von Anwendung Sie entwickeln.
Das erste Beispiel, das Sie zeigen, ist die idiomatische Weise, in der Sie häufig die Hauptmethode eines langlaufenden Threads geschrieben sehen würden. Während es im Allgemeinen wünschenswert ist, ein Mutex- oder Waitable-Ereignissynchronisations-Grundelement anstelle eines Aufrufs an Sleep()
zu verwenden, ist es ansonsten ein typisches Muster, das zum Implementieren von Ereignisverarbeitungsschleifen verwendet wird. Der Vorteil dieses Ansatzes besteht darin, dass die spezielle Verarbeitung in einem separaten Thread ausgeführt werden kann. Dadurch kann der Hauptthread der Anwendung andere Aufgaben ausführen oder auf Benutzereingaben reagieren. Der Nachteil dieses Ansatzes besteht darin, dass möglicherweise Speicherbarrieren (z. B. Sperren) erforderlich sind, um sicherzustellen, dass freigegebene Ressourcen nicht beschädigt werden. Außerdem wird es schwieriger, die Benutzeroberfläche zu aktualisieren, da Sie solche Aufrufe im Allgemeinen an den UI-Thread zurückmelden müssen.
Der zweite Ansatz wird oft auch verwendet - insbesondere in Systemen, die bereits eine Ereignis-Laufwerk-API wie WinForms, WPF oder Silverlight haben. Die Verwendung eines Timer-Objekts oder eines Leerlaufereignisses ist die typische Methode, mit der periodische Hintergrundprüfungen durchgeführt werden können, wenn kein benutzerinitiiertes Ereignis die Verarbeitung auslöst. Der Vorteil hier ist, dass es einfach ist, Benutzeroberflächenobjekte zu interagieren und zu aktualisieren (da sie direkt von demselben Thread aus zugänglich sind), und es verringert die Notwendigkeit von Sperren und Mutexen für geschützte Daten. Ein möglicher Nachteil dieses Ansatzes besteht darin, dass die Verarbeitung, die durchgeführt werden muss, zeitaufwändig ist und dazu führen kann, dass Ihre Anwendung nicht auf Benutzereingaben reagiert.
Wenn Sie keine Anwendungen schreiben, die über eine Benutzeroberfläche verfügen (z. B. Dienste), wird das erste Formular viel häufiger verwendet.
Als Nebensache ... wenn möglich, ist es besser, ein Synchronisationsobjekt wie EventWaitHandle oder Semaphore zu verwenden, um zu signalisieren, wenn Arbeit zur Verarbeitung verfügbar ist. Damit können Sie Thread.Sleep- und/oder Timer-Objekte vermeiden. Es reduziert die durchschnittliche Wartezeit zwischen der Ausführung von Arbeit und der Ausführung von Ereignisverarbeitungscode und minimiert den Overhead bei der Verwendung von Hintergrundthreads, da sie von der Laufzeitumgebung effizienter geplant werden können und keine CPU-Zyklen verbrauchen bis es Arbeit gibt.
Es ist auch erwähnenswert, dass, wenn die Verarbeitung Sie als Reaktion auf die Kommunikation mit externen Quellen (MessageQueues, HTTP, TCP, etc) verwenden Sie Technologien wie WCF verwenden können, um das Skelett Ihres Ereignisbehandlungscodes bereitzustellen. WCF stellt Basisklassen bereit, die die Implementierung von Client- und Server-Systemen, die asynchron auf Kommunikationsereignisaktivitäten reagieren, erheblich vereinfachen.
traurig, dass meine Herangehensweisen am meisten verwendet werden, ich hoffte, dass es dafür eine elegante Art gibt. Aber danke für diese ausführliche Antwort. – Tokk
Aus Neugier, was ist mit diesen Ansätzen, die Sie unelegant finden? – LBushkin
while (true) ist keine elegante Art und Weise imho ;-) – Tokk