Ich schreibe ein Programm, das ein paar Threads hat, jeder mit einer while-Schleife, die ausgeführt wird, bis der Benutzer angibt, dass es aufhören soll. Ich habe mir einige Möglichkeiten ausgedacht, um aus den Schleifen und anschließend den Threads auszutreten, und habe diese Ansätze unten skizziert.Techniken zum Beenden/Abbrechen While-Schleifen über Threads: Bool, ManualResetEvent oder CancellationToken
Fragen
- Gibt es Vor-und Nachteile für jeden?
- Gibt es Fälle, in denen Sie eine und nicht eine andere verwenden würden?
- Ich habe gehört, ein paar Leute sagen, sie bevorzugen CancellationTokens für das Beenden von Threads. Was ist so ansprechend gegenüber den anderen beiden?
Unten folgt die Ansätze.
Der bool
Ansatz
Zunächst ich einen Bool erklärt hatte und erklärte nur, dass die Schleifen laufen, bis die der Benutzer den Bool auf false setzt: while(running) { Thread.Sleep(10); /*do work*/ }
. Ich überlegte dann, ob das ganz threadsicher ist. Was ist, wenn der Compiler eine Optimierung durchgeführt und den bool in ein Register verschoben hat? In diesem Fall würden die Threads unterschiedliche Werte für das Bool sehen. Als Ergebnis habe ich das Bool mit dem Schlüsselwort volatile
markiert, um Compiler-Optimierungen zu vermeiden.
Der ManualResetEvent
Ansatz
Mein nächster Ansatz war ein ManualResetEvent
zu erstellen, und nur sagen, dass der Boolesche läuft, während WaitOne() falsch ist: while(!mre.WaitOne(10)) {/*do work*/}
. Dies wird für 10 ms blockiert, dann wird die Schleife wiederholt, bis wir mre.Set()
tun und die Schleife beendet wird.
Der CancellationToken
Ansatz
Dieser Ansatz habe ich noch nicht wirklich versucht, aber ich lese mehrere Orte, die Menschen auf diese Weise Cancelling Fäden bevorzugen. Es ist scheinbar threadsicher. Man würde definieren eine CancellationTokenSource
, nennen es cts
, dann passieren cts.Token
auf das Verfahren durch den neuen Thread ausgeführt wird und eine if-Anweisung, wenn überprüfen stornieren angefordert wurde: while(!token.IsCancellationRequested) { Thread.Sleep(10); /*do work*/ }
UPDATE 1:
gefunden ein ähnlicher Beitrag, der zu dem Schluss kommt, dass der MRE
Ansatz deutlich langsamer ist als der CancellationToken
Ansatz. Für vollständige Informationen finden Sie hier: Stopping a Thread, ManualResetEvent, volatile boolean or cancellationToken
UPDATE 2:
Wenn es um den Vergleich der bool
Annäherung an die beiden anderen kommt, hat Eric Lippert eine gute Antwort hier: AutoResetEvent vs. boolean to stop a thread
UPDATE 3 :
Ich fand eine andere relevante Information. CancellationTokens kann nach dem Abbrechen nicht zurückgesetzt werden.Es ist also nicht ideal, wenn Sie nur vorübergehend aus einer Schleife ausbrechen möchten, um es später erneut zu starten. Dafür könnte MRE besser sein (wie Sie nach Herzenslust setzen und zurücksetzen können).
Kooperative Kündigung über CTS ist vor allem dann nützlich, wenn Sie andere Methoden aufrufen, die auch die Stornierung unterstützen, da Sie damit das Token in Ihren Anrufbaum weiterleiten können. –
Das MRE-Objekt kann jedoch auch in der Aufrufstruktur übergeben werden. Und der Bool kann ein öffentliches Eigentum sein, in welchem Fall überhaupt kein Passieren erforderlich wäre. – Anders
Ja, aber da das kooperative Auslöschungsmuster ein Standard ist, der von MS in .NET gepusht wird, gibt es mehrere Methoden für BCL-Klassen, die CancellationToken als Parameter unterstützen. Ein Beispiel ist Task.Delay (damit Sie vor Ablauf einer Verzögerung abbrechen können). –