Ich habe eine stilistische Frage zur Auswahl der Hintergrund-Thread-Implementierung, die ich in einer Windows Form-App verwenden sollte. Derzeit habe ich BackgroundWorker
ein Formular mit einer (while(true))
Endlosschleife. In dieser Schleife WaitHandle.WaitAny
döse ich den Thread so lange, bis etwas Interessantes passiert. Eine der Ereignisbehandlungen, auf die ich warte, ist ein " StopThread
" Ereignis, damit ich aus der Schleife ausbrechen kann. Dieses Ereignis wird signalisiert, wenn es von mir überschrieben wird Form.Dispose()
.
Ich habe irgendwo gelesen, BackgroundWorker
was wirklich für Operationen gedacht ist, mit denen Sie die Benutzeroberfläche nicht verknüpfen möchten und die ein endliches Ende haben - wie das Herunterladen einer Datei oder das Verarbeiten einer Folge von Elementen. In diesem Fall ist das "Ende" unbekannt und nur bei geschlossenem Fenster. Wäre es daher angemessener, einen Hintergrund-Thread anstelle BackgroundWorker
dieses Zwecks zu verwenden?
quelle
CancelAsync
(und testen Sie,CancellationPending
ob Ihr Thread in kurzen Intervallen abfragt. Wenn Sie stattdessen eine Ausnahme auslösen möchten, verwenden Sie eineSystem.Threading.Thread.Abort()
, die eine Ausnahme innerhalb des Thread-Blocks selbst auslöst, und wählen Sie das richtige Modell für dieEinige meiner Gedanken ...
quelle
BackgroundWorker
Thread-Fortschritt einer interessierten Partei melden, was normalerweise eine Benutzeroberfläche umfasst. Die MSDN-Dokumentation für die Klasse macht dies sehr deutlich. Wenn Sie lediglich eine Aufgabe im Hintergrund ausführen möchten, verwenden Sie lieber einenThreadPool
Thread.System.Windows.Forms
Versammlung;BackgroundWorker
ist auch für WPF-Apps nützlich und diese Apps haben möglicherweise keinen Verweis auf WinForms.Ziemlich genau das, was Matt Davis gesagt hat, mit den folgenden zusätzlichen Punkten:
Für mich ist das Hauptunterscheidungsmerkmal
BackgroundWorker
das automatische Marshalling des abgeschlossenen Events über dasSynchronizationContext
. In einem UI-Kontext bedeutet dies, dass das abgeschlossene Ereignis im UI-Thread ausgelöst wird und somit zum Aktualisieren der UI verwendet werden kann. Dies ist ein wesentliches Unterscheidungsmerkmal, wenn Sie dasBackgroundWorker
in einem UI-Kontext verwenden.Aufgaben über die Ausführung
ThreadPool
kann nicht einfach gelöscht werden (dies beinhaltetThreadPool
.QueueUserWorkItem
Und Delegierten ausführen asynchron). Während dies den Aufwand für das Hochfahren von Threads vermeidet, verwenden Sie entweder einenBackgroundWorker
oder (wahrscheinlicher außerhalb der Benutzeroberfläche) das Hochfahren eines Threads und behalten Sie einen Verweis darauf, damit Sie ihn aufrufen könnenAbort()
.quelle
Außerdem binden Sie einen Threadpool-Thread für die Lebensdauer des Hintergrundarbeiters, was von Belang sein kann, da es nur eine begrenzte Anzahl von ihnen gibt. Ich würde sagen, wenn Sie den Thread nur einmal für Ihre App erstellen (und keine der Funktionen des Hintergrundarbeiters verwenden), verwenden Sie einen Thread anstelle eines Hintergrundarbeiters / Threadpool-Threads.
quelle
Wissen Sie, manchmal ist es einfacher, mit einem BackgroundWorker zu arbeiten, unabhängig davon, ob Sie Windows Forms, WPF oder eine andere Technologie verwenden. Das Schöne an diesen Jungs ist, dass Sie Threading erhalten, ohne sich zu viele Gedanken darüber machen zu müssen, wo Ihr Thread ausgeführt wird, was sich hervorragend für einfache Aufgaben eignet.
Bevor Sie eine
BackgroundWorker
Überlegung zuerst verwenden, wenn Sie einen Thread abbrechen möchten (App schließen, Benutzer abbrechen), müssen Sie entscheiden, ob Ihr Thread auf Abbrüche prüfen soll oder ob er bei der Ausführung selbst ausgeführt werden soll.BackgroundWorker.CancelAsync()
wird eingestelltCancellationPending
,true
aber es wird nichts mehr getan. Es liegt dann in der Verantwortung des Threads, dies kontinuierlich zu überprüfen. Beachten Sie auch, dass bei diesem Ansatz möglicherweise eine Race-Bedingung auftritt, bei der Ihr Benutzer den Vorgang abgebrochen hat, der Thread jedoch vor dem Testen abgeschlossen wurdeCancellationPending
.Thread.Abort()
Auf der anderen Seite wird eine Ausnahme innerhalb der Thread-Ausführung ausgelöst, die das Abbrechen dieses Threads erzwingt. Sie müssen jedoch vorsichtig sein, was gefährlich sein könnte, wenn diese Ausnahme plötzlich während der Ausführung ausgelöst wird.Das Einfädeln muss unabhängig von der Aufgabe sehr sorgfältig überlegt werden, um weiterzulesen:
Parallele Programmierung in den Best Practices für verwaltetes Threading in .NET Framework
quelle
Ich wusste, wie man Threads verwendet, bevor ich .NET kannte, daher war es etwas gewöhnungsbedürftig, als ich anfing,
BackgroundWorker
s zu verwenden. Matt Davis hat den Unterschied mit großer Exzellenz zusammengefasst, aber ich möchte hinzufügen, dass es schwieriger ist, genau zu verstehen, was der Code tut, und dies kann das Debuggen erschweren. Es ist einfacher, über das Erstellen und Herunterfahren von Threads nachzudenken, IMO, als darüber nachzudenken, einem Pool von Threads Arbeit zu geben.Ich kann die Beiträge anderer Leute immer noch nicht kommentieren, also verzeihen Sie meine momentane Lahmheit, wenn Sie eine Antwort verwenden, um piers7 anzusprechen
Verwenden Sie
Thread.Abort();
stattdessen nicht, signalisieren Sie ein Ereignis und gestalten Sie Ihren Thread so, dass er ordnungsgemäß endet, wenn er signalisiert wird.Thread.Abort()
LöstThreadAbortException
an einem beliebigen Punkt in der Ausführung des Threads ein aus, das alle Arten von unglücklichen Dingen wie verwaiste Monitore, beschädigten gemeinsam genutzten Status usw. ausführen kann.http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx
quelle
Wenn es nicht kaputt ist - reparieren Sie es, bis es ... nur ein Scherz :)
Aber im Ernst, BackgroundWorker ist wahrscheinlich sehr ähnlich zu dem, was Sie bereits haben. Hätten Sie von Anfang an damit begonnen, hätten Sie vielleicht etwas Zeit gespart - aber an diesem Punkt sehe ich keine Notwendigkeit. Wenn etwas nicht funktioniert oder Sie denken, dass Ihr aktueller Code schwer zu verstehen ist, würde ich mich an das halten, was Sie haben.
quelle
Der grundlegende Unterschied besteht, wie Sie bereits sagten, darin, GUI-Ereignisse aus dem zu generieren
BackgroundWorker
. Wenn der Thread die Anzeige nicht aktualisieren oder Ereignisse für den Haupt-GUI-Thread generieren muss, kann es sich um einen einfachen Thread handeln.quelle
Ich möchte auf ein Verhalten der BackgroundWorker-Klasse hinweisen, das noch nicht erwähnt wurde. Sie können einen normalen Thread so einstellen, dass er im Hintergrund ausgeführt wird, indem Sie die Thread.IsBackground-Eigenschaft festlegen.
Sie können dieses Verhalten testen, indem Sie die folgende Methode im Konstruktor Ihres Formularfensters aufrufen.
Wenn die IsBackground-Eigenschaft auf true festgelegt ist und Sie das Fenster schließen, wird Ihre Anwendung normal beendet.
Wenn die IsBackground-Eigenschaft jedoch (standardmäßig) auf false festgelegt ist und Sie das Fenster schließen, wird nur das Fenster ausgeblendet, der Prozess wird jedoch weiterhin ausgeführt.
Die BackgroundWorker-Klasse verwendet einen Thread, der im Hintergrund ausgeführt wird.
quelle
Ein Hintergrund-Worker ist eine Klasse, die in einem separaten Thread arbeitet, aber zusätzliche Funktionen bietet, die Sie mit einem einfachen Thread nicht erhalten (z. B. Behandlung von Aufgabenfortschrittsberichten).
Wenn Sie die zusätzlichen Funktionen eines Hintergrundarbeiters nicht benötigen - und anscheinend auch nicht -, ist ein Thread besser geeignet.
quelle
Was mich verwirrt, ist, dass Sie mit dem Visual Studio Designer nur BackgroundWorker und Timer verwenden können, die mit dem Serviceprojekt nicht funktionieren.
Sie erhalten ordentliche Drag & Drop-Steuerelemente für Ihren Dienst, aber ... versuchen Sie nicht einmal, ihn bereitzustellen. Wird nicht funktionieren.
Dienste: Verwenden Sie nur System.Timers.Timer System.Windows.Forms.Timer funktioniert nicht, obwohl es in der Toolbox verfügbar ist
Dienste: BackgroundWorkers funktionieren nicht, wenn es als Dienst ausgeführt wird. Verwenden Sie stattdessen System.Threading.ThreadPools oder Async-Aufrufe
quelle