Ich möchte einen Thread für eine bestimmte Zeitspanne ausführen. Wenn es nicht innerhalb dieser Zeit abgeschlossen ist, möchte ich es entweder töten, eine Ausnahme auslösen oder auf irgendeine Weise damit umgehen. Wie geht das?
Eine Möglichkeit, wie ich aus diesem Thread herausgefunden habe, besteht darin, eine TimerTask innerhalb der run () -Methode des Threads zu verwenden.
Gibt es dafür bessere Lösungen?
EDIT: Hinzufügen eines Kopfgeldes, da ich eine klarere Antwort brauchte. Der unten angegebene ExecutorService-Code behebt mein Problem nicht. Warum sollte ich nach der Ausführung schlafen () (etwas Code - ich habe kein Handle über diesen Code)? Wenn der Code vollständig ist und sleep () unterbrochen wird, wie kann das ein timeOut sein?
Die Aufgabe, die ausgeführt werden muss, liegt nicht in meiner Kontrolle. Es kann ein beliebiger Code sein. Das Problem ist, dass dieser Code möglicherweise in eine Endlosschleife gerät. Ich möchte nicht, dass das passiert. Ich möchte diese Aufgabe also nur in einem separaten Thread ausführen. Der übergeordnete Thread muss warten, bis dieser Thread beendet ist, und den Status der Aufgabe kennen (dh ob das Zeitlimit überschritten wurde oder eine Ausnahme aufgetreten ist oder ob sie erfolgreich war). Wenn die Aufgabe in eine Endlosschleife gerät, wartet mein übergeordneter Thread unbegrenzt weiter, was keine ideale Situation ist.
quelle
sleep()
war nur ein Stich, um "lange laufende Aufgabe" darzustellen. Ersetzen Sie es einfach durch Ihre eigentliche Aufgabe;)interrupt()
Anrufe in ihrem Thread reagiert ... nicht alle "blockierenden" Anrufe, wie ich in meiner Antwort darauf hinweisen wollte. Die Besonderheiten der Aufgabe, die Sie abbrechen möchten, machen einen großen Unterschied in der Vorgehensweise, die verwendet werden sollte. Weitere Informationen zur Aufgabe wären hilfreich.Antworten:
In der Tat eher verwenden
ExecutorService
alsTimer
, hier ist eine SSCCE :Spielen Sie ein bisschen mit dem
timeout
Argument inFuture#get()
method, z. B. erhöhen Sie es auf 5 und Sie werden sehen, dass der Thread beendet ist. Sie können das Timeout imcatch (TimeoutException e)
Block abfangen .Update: Um ein konzeptionelles Missverständnis zu klären,
sleep()
ist das nicht erforderlich. Es wird nur für SSCCE / Demonstrationszwecke verwendet. Erledige einfach deine langjährige Aufgabe genau dort anstelle vonsleep()
. Innerhalb Ihrer lang laufenden Aufgabe sollten Sie überprüfen, ob der Thread nicht wie folgt unterbrochen wird :quelle
Thread.sleep(4000)
durch eine andere Anweisung mit langer Laufzeit, und das Beispiel funktioniert nicht. Mit anderen Worten, dieses Beispiel würde nur funktionieren , wenn das StatusänderungTask
verstehen sollThread.isInterrupted()
.Es gibt keine 100% zuverlässige Möglichkeit, dies für eine alte Aufgabe zu tun. Die Aufgabe muss unter Berücksichtigung dieser Fähigkeit geschrieben werden.
Kern-Java-Bibliotheken wie das
ExecutorService
Abbrechen asynchroner Aufgaben mitinterrupt()
Aufrufen des Worker-Threads. Wenn die Aufgabe beispielsweise eine Art Schleife enthält, sollten Sie bei jeder Iteration den Interrupt-Status überprüfen . Wenn die Aufgabe E / A-Vorgänge ausführt, sollten diese ebenfalls unterbrechbar sein - und das Einrichten kann schwierig sein. Denken Sie in jedem Fall daran, dass der Code aktiv nach Interrupts suchen muss. Das Setzen eines Interrupts macht nicht unbedingt etwas.Wenn es sich bei Ihrer Aufgabe um eine einfache Schleife handelt, können Sie bei jeder Iteration einfach die aktuelle Uhrzeit überprüfen und aufgeben, wenn eine bestimmte Zeitüberschreitung verstrichen ist. In diesem Fall wird kein Arbeitsthread benötigt.
quelle
execute()
Methode der übergeordneten Schnittstelle führtExecutor
möglicherweise eine Aufgabe im aufrufenden Thread aus. Es gibt keine ähnliche Aussage für diesubmit()
MethodenExecutorService
dieser RückgabeinstanzenFuture
. Der Service impliziert, dass es Arbeitsthreads gibt, die durch Herunterfahren bereinigt werden müssen, und dass Aufgaben asynchron ausgeführt werden. Der Vertrag enthält jedoch nichts, was besagt, dass dieExecutorService
Ausführung von Aufgaben im übergebenden Thread verboten ist. Diese Garantien stammen von den Implementierungs-APIs wieExecutors
Fabriken.Erwägen Sie die Verwendung einer Instanz von ExecutorService . Beide
invokeAll()
undinvokeAny()
Methoden sind mit einemtimeout
Parameter verfügbar .Der aktuelle Thread wird blockiert, bis die Methode abgeschlossen ist (nicht sicher, ob dies wünschenswert ist), entweder weil die Aufgabe (n) normal abgeschlossen wurden oder das Zeitlimit erreicht wurde. Sie können die zurückgegebenen Informationen überprüfen,
Future
um festzustellen, was passiert ist.quelle
Angenommen, der Thread-Code liegt außerhalb Ihrer Kontrolle:
Aus der oben genannten Java- Dokumentation :
Endeffekt:
Stellen Sie sicher, dass alle Threads unterbrochen werden können. Andernfalls benötigen Sie spezielle Kenntnisse über den Thread, z. B. ein Flag zum Setzen. Möglicherweise können Sie verlangen, dass Ihnen die Aufgabe zusammen mit dem Code zum Stoppen übergeben wird - definieren Sie eine Schnittstelle mit einer
stop()
Methode. Sie können auch warnen, wenn Sie eine Aufgabe nicht stoppen konnten.quelle
BalusC sagte:
Aber wenn Sie ersetzen
Thread.sleep(4000);
mitfor (int i = 0; i < 5E8; i++) {}
dann ist es nicht kompilieren, weil die leere Schleife kein wirftInterruptedException
.Und damit der Thread unterbrechbar ist, muss er einen werfen
InterruptedException
.Dies scheint mir ein ernstes Problem zu sein. Ich kann nicht sehen, wie ich diese Antwort anpassen kann, um mit einer allgemeinen langfristigen Aufgabe zu arbeiten.
Bearbeitet, um hinzuzufügen: Ich habe dies als neue Frage erneut beantwortet: [Unterbricht ein Thread nach einer festgelegten Zeit, muss er InterruptedException auslösen ? ]]
quelle
Ich denke, Sie sollten sich die richtigen Mechanismen zur Behandlung von Parallelität ansehen (Threads, die in Endlosschleifen laufen, klingen an sich übrigens nicht gut). Stellen Sie sicher, dass Sie ein wenig über das Thema "Töten" oder "Stoppen" von Threads lesen .
Was Sie beschreiben, klingt sehr nach einem "Rendezvous", daher sollten Sie sich den CyclicBarrier ansehen .
Möglicherweise gibt es andere Konstrukte (wie zum Beispiel die Verwendung von CountDownLatch ), die Ihr Problem lösen können (ein Thread wartet mit einer Zeitüberschreitung auf den Latch, der andere sollte den Latch herunterzählen, wenn er seine Arbeit erledigt hat, wodurch Ihr erster Thread entweder danach freigegeben wird eine Zeitüberschreitung oder wenn der Latch-Countdown aufgerufen wird).
Normalerweise empfehle ich zwei Bücher in diesem Bereich: Concurrent Programming in Java und Java Concurrency in der Praxis .
quelle
Ich habe vor einiger Zeit eine Hilfsklasse dafür erstellt. Funktioniert super:
Es heißt so:
quelle
Ich poste Ihnen einen Code, der zeigt, wie Sie das Problem lösen können. Als Beispiel lese ich eine Datei. Sie können diese Methode für eine andere Operation verwenden, müssen jedoch die kill () -Methode implementieren, damit die Hauptoperation unterbrochen wird.
ich hoffe es hilft
Grüße
quelle
Hier ist meine wirklich einfach zu verwendende Hilfsklasse, um einen Teil des Java-Codes auszuführen oder aufzurufen :-)
Dies basiert auf der hervorragenden Antwort von BalusC
quelle
Das folgende Snippet startet eine Operation in einem separaten Thread und wartet dann bis zu 10 Sekunden, bis die Operation abgeschlossen ist. Wenn der Vorgang nicht rechtzeitig abgeschlossen wird, versucht der Code, den Vorgang abzubrechen, und setzt dann seinen fröhlichen Weg fort. Selbst wenn der Vorgang nicht einfach abgebrochen werden kann, wartet der übergeordnete Thread nicht auf das Beenden des untergeordneten Threads.
Die
getExecutorService()
Methode kann auf verschiedene Arten implementiert werden. Wenn Sie keine besonderen Anforderungen haben, können Sie einfach einExecutors.newCachedThreadPool()
Thread-Pooling ohne Obergrenze für die Anzahl der Threads anfordern .quelle
SomeClass
undFuture
?Eine Sache, die ich nicht erwähnt habe, ist, dass das Töten von Threads im Allgemeinen eine schlechte Idee ist. Es gibt Techniken, um Thread-Methoden sauber abzubrechen , aber das unterscheidet sich davon, einen Thread nach einer Zeitüberschreitung einfach zu .
Das Risiko bei dem, was Sie vorschlagen, besteht darin, dass Sie wahrscheinlich nicht wissen, in welchem Zustand sich der Thread befindet, wenn Sie ihn beenden. Sie riskieren also, Instabilität einzuführen. Eine bessere Lösung besteht darin, sicherzustellen, dass sich Ihr Thread-Code entweder nicht selbst aufhängt oder auf eine Abbruchanforderung gut reagiert.
quelle
Tolle Antwort von BalusC's:
Aber nur um hinzuzufügen, dass das Timeout selbst den Thread selbst nicht unterbricht. auch wenn Sie mit while (! Thread.interrupted ()) in Ihrer Aufgabe prüfen. Wenn Sie sicherstellen möchten, dass der Thread gestoppt wird, sollten Sie auch sicherstellen, dass future.cancel () aufgerufen wird, wenn die Timeout-Ausnahme catch ist.
quelle
Ich denke, die Antwort hängt hauptsächlich von der Aufgabe selbst ab.
Wenn die erste Antwort Ja und die zweite Nein lautet, können Sie dies so einfach halten:
Wenn dies keine Option ist, schränken Sie bitte Ihre Anforderungen ein - oder zeigen Sie Code an.
quelle
Ich habe nach einem ExecutorService gesucht, der alle von ihm ausgeführten Runnables mit Zeitüberschreitung unterbrechen kann, aber keine gefunden hat. Nach ein paar Stunden habe ich eine wie unten erstellt. Diese Klasse kann geändert werden, um die Robustheit zu verbessern.
Verwendung:
quelle
Jetzt treffe ich ein Problem wie dieses. Es passiert das Bild zu dekodieren. Der Dekodierungsprozess dauert zu lange, als dass der Bildschirm schwarz bleibt. lch füge einen Zeitregler hinzu: Wenn die Zeit zu lang ist, öffne dich aus dem aktuellen Thread. Das Folgende ist der Unterschied:
quelle
Ich hatte das gleiche Problem. Also habe ich mir eine einfache Lösung wie diese ausgedacht.
Garantiert, dass der Block nicht innerhalb des Zeitlimits ausgeführt wurde. Der Prozess wird beendet und eine Ausnahme ausgelöst.
Beispiel:
quelle
In der von BalusC angegebenen Lösung bleibt der Haupt-Thread für die Zeitüberschreitung blockiert. Wenn Sie einen Thread-Pool mit mehr als einem Thread haben, benötigen Sie dieselbe Anzahl zusätzlicher Threads, die Future.get verwenden (lange Zeitüberschreitung, TimeUnit-Einheit). Blockierungsaufruf , um zu warten und den Thread zu schließen, wenn der Timeout-Zeitraum überschritten wird.
Eine generische Lösung für dieses Problem besteht darin, einen ThreadPoolExecutor Decorator zu erstellen, der die Timeout-Funktionalität hinzufügen kann. Diese Decorator-Klasse sollte so viele Threads erstellen wie ThreadPoolExecutor, und alle diese Threads sollten nur zum Warten und Schließen des ThreadPoolExecutor verwendet werden.
Die generische Klasse sollte wie folgt implementiert werden:
Der obige Dekorateur kann wie folgt verwendet werden:
quelle