Frage

Ich bin derzeit auf der Suche nach einem bösen Fehler in einer Multithread-Umgebung, die FutureTasks und Executors verwendet.Die Grundidee besteht darin, eine feste Anzahl von Threads einzelne FutureTasks ausführen zu lassen, die ein Ergebnis berechnen, das in einer Tabelle angezeigt werden soll (ganz zu schweigen vom GUI-Aspekt).

Ich habe mir das schon so lange angesehen, dass ich langsam an meinem Verstand zweifele.

Betrachten Sie diesen Code:

public class MyTask extends FutureTask<Result> {
   private String cellId;
   ...
   protected void done() {
      if (isCancelled()) return;
      try {
          Result r = get(); // should not wait, because we are done
          ... // some processing with r
          sendMessage(cellId, r);
      } catch (ExecutionException e) { // thrown from get
         ... 
      } catch (InterruptedException e) { // thrown from get
         ... 
      }
   }
   ...
}

Wann done() von einem Executor aufgerufen wird, der eine Instanz von MyTask verarbeitet, überprüfe ich, ob ich dort angekommen bin, da die Aufgabe abgebrochen wurde.Wenn ja, überspringe ich alle verbleibenden Aktivitäten, insbesondere rufe ich nicht an sendMessage().

In der Dokumentation zu FutureTask.done() heißt es:

Geschützte Methode, die aufgerufen wird, wenn diese Aufgabe in den Status isDone übergeht (ob normal oder durch Abbruch).Die Standardimplementierung führt nichts aus.Unterklassen können diese Methode überschreiben, um Abschlussrückrufe aufzurufen oder die Buchhaltung durchzuführen.Beachten Sie, dass Sie den Status innerhalb der Implementierung dieser Methode abfragen können, um festzustellen, ob diese Aufgabe abgebrochen wurde.(API-Referenz)

Was ich aber aus der Dokumentation nicht verstehe FutureTask sind die Semantik während done() wird ausgeführt.Was ist, wenn ich das bestehe isCancelled() Überprüfen Sie es am Anfang, aber gleich danach ruft ein anderer Thread meine an cancel() Methode?Wird das dazu führen, dass meine Aufgabe ihre Meinung und Antwort ändert? isCancelled() == true von da an?

Wenn ja, woher weiß ich später, ob die Nachricht gesendet wurde?Anschauen isDone() Ich würde mir nur sagen, dass die Ausführung der Aufgabe abgeschlossen ist, aber wie isCancelled() damals auch wahr waren, konnte ich nicht wissen, ob es die Nachricht noch rechtzeitig verschicken konnte.

Vielleicht ist das offensichtlich, aber ich sehe es im Moment nicht wirklich.

War es hilfreich?

Lösung

Aus der API (Hervorhebung von mir):

öffentlicher boolescher Abbruch (boolean mayInterruptIfRunning)

Beschreibung von der Schnittstelle kopiert:Zukunft

Versucht, die Ausführung dieser Aufgabe abzubrechen. Dieser Versuch schlägt fehl, wenn die Aufgabe bereits abgeschlossen wurde, bereits storniert wurde oder aus einem anderen Grund nicht storniert werden konnte.

FutureTask geht also davon aus, dass Sie eine Aufgabe nicht abbrechen können, wenn sie in die Phase „isDone“ übergegangen ist.

Andere Tipps

FutureTask#done() heißt nicht mehr als einmal für jede gegebene Beispiel, und es ist nur aus einem Grund genannt - run() abgeschlossen entweder mit oder ohne Fehler oder cancel() lief vor einem der vorhergehenden Ereignisse aufgetreten sind. Die Aufzeichnung der Fertigstellung durch eines dieser Ergebnisse ist Rast . Der Grund, ein FutureTask abgeschlossen nicht ändern kann, und zwar unabhängig von Ereignissen scheinbar geschehen im Wettbewerb „zugleich.“

Daher innerhalb FutureTask#done() nur eine von isCancelled() oder isDone() wird return true dann und immer mehr. Es ist schwierig, zwischen isDone() Berichterstattung wahr haft Fehlern oder erfolgreichen Abschluss zu unterscheiden. Sie können nicht set() oder setException(Throwable) außer Kraft entscheidend, sowohl als Delegierter an den inneren AQS , um zu entscheiden, ob der Versuch ein erfolgreiches Nachgeben eines Wertes zu erfassen oder eine Ausnahme zu stoßen sollte bleiben. nur entweder Methode überschrieben lassen Sie wissen, dass es genannt wurde, aber man kann die Entscheidung der Basisimplementierung nicht beobachten. Wenn entweder Ereignis eintritt „zu spät“ -Say, nach Storno der Versuch, den Wert oder die Ausnahme zu erfassen, werden ignoriert.

Das Studium der Implementierung, der einzige Weg, ich sehe eine nicht abgebrochen erfolgreiches Ergebnis zu unterscheiden von einem Fehler den sauren Apfel beißen und get() nennen.

Warum nicht die Meldung "außerhalb" der Aufgabe senden, basierend auf dem Ergebnis der Future Objekt zurückgegeben durch ein ExecutorService ? Ich habe dieses Muster verwendet, und es scheint gut zu funktionieren: Einen Haufen von Callable Aufgaben durch ein ExecutorService . Dann wird für jede primäre Aufgabe, senden Sie eine sekundäre Aufgabe, die auf dem Future wartet der primären Aufgabe und hat einige Folgemaßnahmen (wie eine Nachricht senden) nur, wenn der Future gibt an, dass die primäre Aufgabe erfolgreich abgeschlossen. Es gibt keine Vermutungen mit diesem Ansatz. Wenn der Aufruf von Future .get () zurückzugibt, sind Sie garantiert, dass die Aufgabe, einen Endzustand erreicht hat, solange Sie nicht die Version von nennen erhalten , das ein Timeout-Argument.

Wenn Sie diesen Ansatz nehmen, sollten Sie zwei separate verwenden ExecutorService Instanzen: eine für die Hauptaufgaben und eine für die Sekundär diejenigen. Dies ist zu Deadlocks zu verhindern. Sie haben keine Nebenaufgaben beginnen sollen und möglicherweise primäre Aufgaben blockieren anspringt, wenn die Threadpoolgröße ist begrenzt.

Es gibt keine Notwendigkeit, FutureTask überhaupt zu erweitern. implementieren Sie einfach Ihre Aufgaben als Callable Objekte. Aber wenn Sie aus irgendeinem Grund, wenn die Aufgabe innerhalb der Callable abgebrochen wurde erkannt werden soll Code, überprüfen Sie die Interrupt-Status des Threads mit Thread.interrupted () .

Ich schlage vor, einen kleinen Testfall zu schreiben, die Sie cancel() während Future Instanz hängt in done() aufrufen können und sehen, was passiert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top