Domanda

Attualmente sto cercando un brutto bug in un ambiente multi-thread usando FutureTasks ed Executors. L'idea di base è che un numero fisso di thread esegua singoli FutureTasks che calcolano un risultato che deve essere visualizzato in una tabella (non importa l'aspetto della GUI qui).

Lo guardo da così tanto tempo, sto cominciando a dubitare della mia sanità mentale.

Considera questo pezzo di codice:

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

Quando done() viene chiamato da un Executor che gestisce un'istanza di MyTask, controllo se ci sono arrivato perché l'attività è stata annullata. In tal caso, ignoro tutte le attività rimanenti, in particolare non chiamo sendMessage().

La documentazione per FutureTask.done () dice:

  

Metodo protetto invocato quando questa attività passa allo stato isDone (normalmente o tramite annullamento). L'implementazione predefinita non fa nulla. Le sottoclassi possono ignorare questo metodo per richiamare i callback di completamento o eseguire la contabilità. Si noti che è possibile richiedere lo stato all'interno dell'implementazione di questo metodo per determinare se questa attività è stata annullata.   ( Riferimento API )

Ma ciò che non ottengo dalla documentazione di FutureTask sono la semantica mentre isCancelled() è in esecuzione. Cosa succede se passo il controllo cancel() all'inizio, ma subito dopo che qualche altro thread chiama il mio metodo isCancelled() == true? Ciò farà cambiare idea al mio compito e risponderà isDone() da allora in poi?

In tal caso, come potrei in seguito sapere se il messaggio è stato inviato? Guardare <=> mi direbbe solo che l'esecuzione dell'attività era terminata, ma poiché <=> era vero anche allora, non riuscivo a sapere se fosse possibile inviare il messaggio in tempo.

Forse questo è ovvio, ma non lo vedo proprio ora.

È stato utile?

Soluzione

Dall'API (l'enfasi è mia):

  

cancellazione booleana pubblica (boolean mayInterruptIfRunning)

     

Descrizione copiata dall'interfaccia: Future

     

Tenta di annullare l'esecuzione di questa attività. Questo tentativo fallirà se l'attività è già stata completata , già annullata o non può essere annullata per qualche altro motivo.

Quindi FutureTask sta lavorando sul presupposto che non è possibile annullare un'attività quando è passata allo stage isDone.

Altri suggerimenti

< => non viene chiamato più di una volta per una determinata istanza e viene chiamato solo per un motivo: FutureTask#done() completato con o senza errore o run() eseguito prima che si verificasse uno degli eventi precedenti. Il record di completamento di uno di questi risultati è latching . Il motivo per cui un cancel() completato non può cambiare, indipendentemente dagli eventi in competizione che sembrano accadere & Quot; allo stesso tempo. & Quot;

Quindi, entro FutureTask solo uno di isCancelled() o isDone() tornerà vero allora e per sempre di più. È difficile distinguere tra set() la segnalazione vera per errore o completamento riuscito. Non è possibile eseguire l'override setException(Throwable) o AQS in modo decisivo, poiché entrambi delegano l'interno get() per decidere se il tentativo di registrare un cedimento di un valore riuscito o riscontrare un'eccezione dovrebbe restare. L'override di entrambi i metodi consente solo di sapere che è stato chiamato, ma non è possibile osservare la decisione presa dall'implementazione di base. Se si verifica uno dei due eventi & Quot; troppo tardi & Quot; & # 8212; diciamo dopo cancellazione & # 8212; il tentativo di registrare il valore o l'eccezione sarà ignorato.

Studiando l'implementazione, l'unico modo che vedo per discernere un esito positivo non cancellato da un errore è mordere il proiettile e chiamare <=>.

Perché non inviare il messaggio " outside " dell'attività, in base al risultato dell'oggetto Future < V > restituito da un Servizio Executor ? Ho usato questo modello e sembra funzionare bene: Invia un gruppo di Callable & Lt; V & Gt; tramite un Servizio Executor . Quindi, per ogni attività primaria, invia un'attività secondaria che attende il Futuro & Lt; V & Gt; dell'attività primaria e fa alcune azioni di follow-up (come inviare un messaggio ) solo se il Futuro < V > indica che l'attività principale è stata completata correttamente. Non ci sono congetture con questo approccio. Quando ritorna la chiamata a Futuro & Lt; V & Gt; .get () , sei certo che l'attività ha raggiunto uno stato terminale, purché non chiami la versione di get che accetta un argomento di timeout.

Se segui questo approccio, dovresti utilizzare due istanze ExecutorService separate: una per le attività primarie e una per quelle secondarie. Questo per prevenire deadlock. Non si desidera avviare attività secondarie e potenzialmente bloccare l'avvio delle attività primarie quando la dimensione del pool di thread è limitata.

Non è necessario estendere FutureTask < V > . Basta implementare le attività come Callable & Lt; V & Gt; . Ma se per qualche motivo vuoi rilevare se l'attività è stata annullata dal codice Callable & Lt; V & Gt; , controlla lo stato di interruzione del thread con Thread.interrupted () .

Suggerisco di scrivere un piccolo test case che ti consenta di chiamare cancel() mentre la tua Future istanza si blocca in done() e vedere cosa succede.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top