Question

Je cherche actuellement un vilain bogue dans un environnement multithread utilisant FutureTasks and Executors. L'idée de base est de faire en sorte qu'un nombre fixe de threads exécutent chaque tâche FutureTasks individuelle qui calcule un résultat à afficher dans une table (peu importe l'aspect de l'interface graphique ici).

Je regarde ça depuis si longtemps que je commence à douter de ma santé mentale.

Considérez ce morceau de 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
         ... 
      }
   }
   ...
}

Lorsque done() est appelé par un exécuteur gérant une instance de MyTask, je vérifie si j'y suis arrivé, car la tâche a été annulée. Si c'est le cas, je saute toutes les activités restantes, en particulier je n'appelle pas sendMessage().

La documentation de FutureTask.done () indique:

  

Méthode protégée invoquée lors du passage de cette tâche à l'état isDone (normalement ou via annulation). L'implémentation par défaut ne fait rien. Les sous-classes peuvent remplacer cette méthode pour appeler des rappels d'achèvement ou effectuer une tenue de livre. Notez que vous pouvez interroger le statut dans l'implémentation de cette méthode pour déterminer si cette tâche a été annulée.   ( Référence de l'API )

Mais ce que je ne comprends pas dans la documentation de FutureTask, ce sont les sémantiques pendant l’exécution de isCancelled(). Que se passe-t-il si je passe la vérification cancel() au début, mais juste après, un autre thread appelle ma méthode isCancelled() == true? Est-ce que cela va faire changer d'avis et donner une réponse isDone() à partir de ce moment-là?

Si oui, comment pourrais-je savoir plus tard si le message a été envoyé? En regardant <=>, cela me dirait simplement que l'exécution de la tâche est terminée, mais comme <=> étaient également vrais, je ne pouvais pas savoir s'il était possible d'envoyer le message à temps.

C'est peut-être évident, mais je ne le vois pas vraiment pour l'instant.

Était-ce utile?

La solution

À partir de l'API (l'emphase mienne):

  

public boolean cancel (boolean mayInterruptIfRunning)

     

Description copiée de l'interface: Future

     

Essaie d'annuler l'exécution de cette tâche. Cette tentative échouera si la tâche est déjà terminée , a déjà été annulée ou ne peut pas l'être pour une autre raison.

Donc, FutureTask travaille sur l'hypothèse selon laquelle vous ne pouvez pas annuler une tâche lorsqu'elle est passée à l'étape isDone.

Autres conseils

< => n'est appelé qu'une seule fois pour une instance donnée, et ce pour une seule raison - FutureTask#done() terminé avec ou sans erreur, ou run() exécuté avant l'un des événements précédents. L’un des résultats obtenus par l’un de ces résultats est verrouillé . La raison pour laquelle un cancel() achevé ne peut pas changer, quels que soient les événements concurrents qui se produisent apparemment en même temps. & "; Au même moment. &";

Par conséquent, à l'intérieur de FutureTask seul un de isCancelled() ou isDone() deviendra vrai à ce moment et pour toujours plus. Il est difficile de faire la distinction entre set() les rapports vrais par erreur ou par achèvement. Vous ne pouvez pas écraser setException(Throwable) ou AQS de manière décisive, car les deux délégués délèguent à l'intérieur get() pour décider si la tentative d’enregistrer une cession réussie d’une valeur ou de rencontrer une exception doit rester bloquée. Remplacer l'une ou l'autre des méthodes vous indique uniquement que l'appel a été appelé, mais vous ne pouvez pas observer la décision prise par l'implémentation de base. Si l'un ou l'autre événement se produit & "Trop tard &"; & # 8212; disons après l'annulation & # 8212; la tentative d'enregistrement de la valeur ou de l'exception sera ignoré.

En étudiant l’implémentation, la seule façon de discerner le résultat positif d’une erreur non annulée est de mordre la balle et d’appeler <=>.

Pourquoi ne pas envoyer le message & "en dehors de &"; de la tâche, en fonction du résultat de l'objet Future < V > renvoyé par un ServeurExecutor ? J'ai utilisé ce modèle et il semble bien fonctionner: soumettez un ensemble de tâches appelables & Lt; V & Gt; via un service d'exécution . Ensuite, pour chaque tâche principale, soumettez une tâche secondaire qui attend le avenir & Lt; V & Gt; de la tâche principale et effectuez des actions de suivi (comme envoyer un message). ) uniquement si le avenir < V > indique que la tâche principale a été exécutée avec succès. Il n'y a pas de devinettes avec cette approche. Lorsque l'appel à Future & Lt; V & Gt; .get () est rétabli, vous êtes assuré que la tâche a atteint l'état du terminal, tant que vous n'appelez pas. la version de get qui prend un argument de délai d'attente.

Si vous utilisez cette approche, vous devez utiliser deux instances ExecutorService distinctes: une pour les tâches principales et une pour les tâches secondaires. C'est pour éviter les impasses. Vous ne voulez pas que les tâches secondaires démarrent et empêchent éventuellement le démarrage des tâches principales lorsque la taille du pool de threads est limitée.

Il n'est pas nécessaire d'étendre FutureTask < V > . Implémentez simplement vos tâches en tant qu'objets appelables & Lt; V & Gt; . Mais si, pour une raison quelconque, vous voulez détecter si la tâche a été annulée depuis le code Callable & Lt; V & Gt; , vérifiez simplement le statut d'interruption du thread avec . Thread.interrupted () .

Je suggère d'écrire un petit scénario de test vous permettant d'appeler cancel() pendant que votre Future instance se bloque dans done() et de voir ce qui se passe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top