6

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

  1. Gibt es Vor-und Nachteile für jeden?
  2. Gibt es Fälle, in denen Sie eine und nicht eine andere verwenden würden?
  3. 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).

+0

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. –

+0

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

+1

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). –

Antwort

2

Meistens laufen Ihre Threads nicht in engen Schleifen, essen alle Ihre CPU-Zyklen, oft warten Sie auf Ereignisse irgendeiner Art, wenn Sie warten, können Sie nicht wirklich auf einen Bool warten. Du könntest eine Zeitüberschreitung bei deiner Veranstaltung haben, auf die Zeitüberschreitung warten, die bool überprüfen und dann zurück zum Warten gehen. Dies führt zu bösartigem Code und bedeutet auch, dass Ihr Thread nicht beendet wird, bis Timeouts auftreten, oder Sie essen, dass Ihre CPU einen Bool prüft.

Reset Event ist in Ordnung, man kann sicherlich damit arbeiten, aber CancellellationToken funktioniert gut und ist genau dafür ausgelegt.

+0

Die App, die ich schreibe, erfordert, dass ich einige Bedingungen überprüfe, die außerhalb meiner App liegen. Daher gibt es keine Ereignisse oder irgendetwas, das ich hinzufügen kann, um sicherzustellen, dass es mich über Änderungen informiert. Ich muss eine Art Umfrage machen. Anstatt die while-Schleifen zu verwenden, könnte man einen Timer verwenden, der regelmäßig mit regelmäßigen Intervallen aus dem Thread-Pool abfragt (ich habe diesen Ansatz gegebenenfalls verwendet). Das ist ein bisschen außerhalb des Rahmens dieses Beitrags, aber es geht eher um den Vergleich von Abbruch-Techniken. – Anders