Warum wird der Thread nicht unterbrochen, wenn er in den Block schlafe?

8

Ich habe überall auf MSDN gesucht und kann keinen Grund finden, warum Thread nicht unterbrochen werden kann, wenn ich innerhalb von blockiere. Ich habe versucht, ohne Erfolg abzubrechen.

Gibt es eine Möglichkeit, Thread zu wecken, wenn Sie innerhalb von Block schlie? en?

%Vor%

Überraschenderweise behauptet MSDN, dass Thread in abschließendem Block abgebrochen werden kann: Ссылка "Es besteht die Möglichkeit, dass der Thread abbricht, während ein finally-Block ausgeführt wird. In diesem Fall wird der finally-Block abgebrochen."

Bearbeiten Ich finde Hans-Passant-Kommentar als beste Antwort, da dies erklärt warum Thread manchmal nicht unterbrochen / abgebrochen werden kann im Endblock. Und dann wird der Prozess heruntergefahren. Danke

    
Marek 15.08.2011, 12:19
quelle

2 Antworten

8

Das Abbrechen und Unterbrechen von Threads sollte nach Möglichkeit vermieden werden, da dies den Zustand eines laufenden Programms beeinträchtigen kann. Angenommen, Sie haben einen Thread abgebrochen, der Sperren für Ressourcen geöffnet hat. Diese Sperren werden niemals freigegeben.

Ziehen Sie stattdessen die Verwendung eines Signalisierungsmechanismus in Betracht, damit Threads miteinander kooperieren können und das Blockieren und Entsperren elegant behandeln, z. B .:

%Vor%

Aktualisieren

Ziemlich interessantes Low-Level-Problem. Obwohl Abort() dokumentiert ist, ist Interrupt() viel weniger.

Die kurze Antwort auf Ihre Frage lautet nein, Sie können einen Thread in einem finally-Block nicht aufwecken, indem Sie Abort oder Interrupt darauf aufrufen.

In der Lage ist, Threads in final Blöcken nicht abzubrechen oder zu unterbrechen, ist von Entwurf, einfach so, dass Blöcke schließlich eine Chance haben, wie Sie erwarten, zu laufen. Wenn Sie Threads in Endblocks abbrechen und unterbrechen könnten, könnte dies unbeabsichtigte Konsequenzen für Aufräumroutinen haben und die Anwendung in einem beschädigten Zustand belassen - nicht gut.

Eine leichte Nuance mit Threadunterbrechung ist, dass ein Interrupt möglicherweise gegen den Thread ausgegeben wurde, bevor er in den finally-Block eingetreten ist, aber während er nicht in einem SleepWaitJoin -Zustand war (d. h. nicht blockiert). Wenn in diesem Fall ein blockierender Aufruf im finally-Block vorhanden wäre, würde sofort ein ThreadInterruptedException geworfen und der finally-Block abgebrochen. Schließlich verhindert Blockschutz dies.

Neben dem Schutz in finally-Blöcken erstreckt sich dies auch auf try-Blöcke und auch CERs ( Constrained Execution Region ), die im Benutzercode konfiguriert werden kann, um zu verhindern, dass eine Reihe von Ausnahmen ausgelöst wird, nachdem eine Region ausgeführt wurde - sehr nützlich für kritische Codeblöcke, die müssen abgeschlossen werden und den Abbruch verzögern.

Die Ausnahme (kein Wortspiel beabsichtigt) dazu sind sogenannte Rude Aborts . Dies sind ThreadAbortExceptions , die von der CLR-Hosting-Umgebung selbst ausgelöst werden. Diese können dazu führen, dass endlich und catch Blöcke verlassen werden, aber nicht CERs. Zum Beispiel kann die CLR unhöfliche Abtreibungen in Reaktion auf Threads auslösen, von denen sie festgestellt hat, dass sie zu lange dauern, um ihre Arbeit zu erledigen, z. beim Versuch, eine AppDomain zu entladen oder Code in der SQL Server-CLR auszuführen. Wenn Ihre Anwendung in Ihrem speziellen Beispiel heruntergefahren wird und die Anwendungsdomäne auslädt, gibt die CLR für den schlafenden Thread einen unhöflichen Abbruch aus, da ein Überladungstimeout für die Anwendungsdomäne vorhanden wäre.

Das Abbrechen und Unterbrechen von Blöcken wird nicht im Benutzercode passieren, aber es gibt ein etwas anderes Verhalten zwischen den beiden Fällen.

Abbrechen

Beim Aufrufen von Abort für einen Thread in einem finally-Block wird der aufrufende Thread blockiert. Dies ist dokumentiert :

  

Der Thread, der Abort aufruft, blockiert möglicherweise, wenn sich der Thread, der abgebrochen wird, in einer geschützten Code-Region befindet, z. B. einem catch-Block, einem finally-Block oder einem eingeschränkten Ausführungsbereich.

Im Abbruchfall, wenn der Schlaf nicht unendlich war:

  1. Der aufrufende Thread gibt ein Abort aus, blockiert jedoch hier, bis der finally-Block beendet wird, d. h. er stoppt hier und geht nicht sofort zur Join -Anweisung über.
  2. Der Status des angerufenen Threads ist auf AbortRequested festgelegt.
  3. Der Angerufene schläft weiter.
  4. Wenn der Angerufene aufwacht, wird er, da er den Status AbortRequested hat, den abschließenden Blockcode ausführen und dann "verdampfen", d. h. beenden.
  5. Wenn der abgebrochene Thread den finally-Block verlässt: Es wird keine Ausnahme ausgelöst, nach dem finally-Block wird kein Code ausgeführt, und der Thread-Status lautet Aborted .
  6. Der aufrufende Thread wird entsperrt, fährt mit der Join -Anweisung fort und wird sofort übergeben, sobald der aufgerufene Thread beendet wurde.

Wenn Ihr Beispiel einen unendlichen Schlaf hat, wird der aufrufende Thread für immer bei Schritt 1 blockieren.

Unterbrechen

Im Unterbrechungsfall, wenn der Schlaf nicht unendlich war:

Nicht so gut dokumentiert ...

  1. Der aufrufende Thread gibt Interrupt aus und weiter wird ausgeführt.
  2. Der aufrufende Thread blockiert die Join -Anweisung.
  3. Der Status des angerufenen Threads ist so eingestellt, dass beim nächsten blockierenden Aufruf eine Ausnahme ausgelöst wird, aber entscheidend ist, dass er in einem finally-Block nicht entsperrt ist, d. h. aufgewacht ist.
  4. Der Angerufene schläft weiter.
  5. Wenn der Angerufene aufwacht, wird die Ausführung des finally-Blocks fortgesetzt.
  6. Wenn der unterbrochene Thread den finally-Block verlässt, wirft er bei seinem nächsten blockierenden Aufruf ThreadInterruptedException (siehe Codebeispiel unten).
  7. Der aufrufende Thread "verbindet" und wird fortgesetzt, sobald der aufgerufene Thread beendet wurde. Der nicht behandelte ThreadInterruptedException in Schritt 6 hat nun jedoch den Prozess abgeflacht ...

Wenn Sie also Ihr Beispiel mit einem unendlichen Schlaf versehen haben, blockiert der aufrufende Thread für immer, aber in Schritt 2.

Zusammenfassung

Obwohl Abort und Interrupt ein etwas anderes Verhalten haben, führen beide dazu, dass der aufgerufene Thread für immer schläft und der aufrufende Thread für immer blockiert (in Ihrem Beispiel).

Nur ein unhöflicher Abbruch kann einen blockierten Thread dazu zwingen, einen finally-Block zu verlassen, und diese können nur von der CLR selbst ausgelöst werden (Sie können nicht einmal die Reflektion verwenden, um ThreadAbortException.ExceptionState zu diddle, da sie einen internen CLR-Aufruf ausführt, co_de% - keine Gelegenheit, dort leicht böse zu sein ...).

Die CLR verhindert, dass Benutzercode dazu führt, dass die endgültigen Blöcke zu unserem eigenen Vorteil vorzeitig beendet werden - dies hilft, einen beschädigten Zustand zu verhindern.

Ein Beispiel für das etwas andere Verhalten mit AbortReason :

%Vor%     
Tim Lloyd 15.08.2011, 12:44
quelle
4

Der ganze Punkt eines finally -Blocks besteht darin, etwas zu halten, das nicht von einem Interrupt betroffen ist oder abbricht und unabhängig von der Operation normal abläuft. Die Erlaubnis, dass ein finally -Block abgebrochen oder unterbrochen wird, würde den Punkt ziemlich besiegen. Wie Sie bereits bemerkt haben, können finally Blöcke leider aufgrund verschiedener Rennbedingungen abgebrochen oder unterbrochen werden. Deshalb werden Sie viele Leute sehen, die Ihnen raten, Threads nicht zu unterbrechen oder abzubrechen.

Verwenden Sie stattdessen kooperatives Design. Wenn ein Thread unterbrochen werden soll, verwenden Sie anstelle des Aufrufs von Sleep eine zeitgesteuerte Wartezeit. Anstatt Interrupt zu signalisieren, wird auf den Thread gewartet, auf den der Thread wartet.

    
David Schwartz 15.08.2011 12:33
quelle