Pregunta

Actualmente estoy a la caza de un desagradable error en un entorno multiproceso utilizando FutureTasks y los Ejecutores.La idea básica es que esto de tener un número fijo de hilos de ejecución individual FutureTasks que calcular un resultado que se muestra en una tabla (no importa la interfaz de usuario de aspecto aquí).

He estado mirando esto por tanto tiempo, estoy empezando a dudar de mi cordura.

Considere la posibilidad de este trozo de código:

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

Cuando done() es llamado por un Ejecutor de manejo de una instancia de MyTask, puedo comprobar si llegué allí, ya que la tarea fue cancelado.Si es así, me salto el resto de actividades, especialmente no me llama sendMessage().

La documentación para FutureTask.hecho() dice:

Protegido método que se invoca cuando esta tarea transiciones de estado isDone (ya sea normalmente o a través de la cancelación).La implementación por defecto no hace nada.Las subclases pueden invalidar este método para invocar la finalización de las devoluciones de llamada o realizar la contabilidad.Tenga en cuenta que usted puede consultar el estado dentro de la aplicación de este método para determinar si la tarea ha sido cancelado.(Referencia de la API)

Pero lo que no entiendo de la documentación de FutureTask son la semántica mientras done() está siendo ejecutado.Lo que si me pase de la isCancelled() verificación al principio, pero justo después de que otro subproceso llama a mi cancel() método?Se que la causa de mi tarea para cambiar su mente y respuesta isCancelled() == true a partir de entonces?

Si es así, ¿cómo puedo posterior saber si el mensaje fue enviado?Mirando isDone() acaba de decirme que la ejecución de la tarea estaba terminada, pero como isCancelled() era verdad entonces, yo no podía saber si lo tengo que enviar el mensaje en el tiempo.

Tal vez esto es obvio, pero yo realmente no lo veo justo ahora.

¿Fue útil?

Solución

Desde la API (énfasis mío):

  

cancelación booleana pública (boolean mayInterruptIfRunning)

     

Descripción copiada de la interfaz: Futuro

     

Intenta cancelar la ejecución de esta tarea. Este intento fallará si la tarea ya se completó , ya se canceló o no se pudo cancelar por algún otro motivo.

De modo que FutureTask está funcionando bajo el supuesto de que no puede cancelar una tarea cuando ha pasado a la etapa isDone.

Otros consejos

< => no se llama más de una vez para una instancia determinada, y solo se llama por una razón: FutureTask#done() completado con o sin error, o run() se ejecutó antes de que ocurriera cualquiera de los eventos anteriores. El registro de finalización de cualquiera de estos resultados es enclavamiento . La razón por la que un cancel() completado no puede cambiar, independientemente de los eventos en competencia que aparentemente suceden & Quot; al mismo tiempo. & Quot;

Por lo tanto, dentro de FutureTask solo uno de isCancelled() o isDone() devolverá verdadero entonces y para siempre más. Es difícil distinguir entre set() informes verdaderos por error o finalización exitosa. No puede anular setException(Throwable) o AQS decisivamente, ya que ambos delegan al get() para decidir si el intento de registrar un rendimiento exitoso de un valor o encontrar una excepción debe mantenerse. Anular cualquiera de los métodos solo le permite saber que se llamó, pero no puede observar la decisión tomada por la implementación base. Si cualquiera de los eventos ocurre & Quot; demasiado tarde & Quot; & # 8212; diga, después de cancelación & # 8212; el intento de registrar el valor o la excepción será ignorado.

Al estudiar la implementación, la única forma en que veo distinguir un resultado exitoso no cancelado de un error es morder la viñeta y llamar a <=>.

¿Por qué no enviar el mensaje " fuera de " de la tarea, según el resultado del objeto Future < V > devuelto por un ExecutorService ? He usado este patrón y parece funcionar bien: envíe un montón de tareas invocables & Lt; V & Gt; a través de un ExecutorService . Luego, para cada tarea principal, envíe una tarea secundaria que espere en el Futuro & Lt; V & Gt; y realice alguna acción de seguimiento (como enviar un mensaje ) solo si Future < V > indica que la tarea principal se completó correctamente. No hay conjeturas con este enfoque. Cuando vuelve la llamada a Future & Lt; V & Gt; .get () , tiene la garantía de que la tarea ha alcanzado un estado terminal, siempre que no llame la versión de get que requiere un argumento de tiempo de espera.

Si adopta este enfoque, debe utilizar dos instancias separadas de ExecutorService : una para las tareas principales y otra para las secundarias. Esto es para evitar puntos muertos. No desea que se inicien las tareas secundarias y potencialmente bloquee el inicio de las tareas principales cuando el tamaño del grupo de subprocesos es limitado.

No es necesario extender FutureTask < V > en absoluto. Simplemente implemente sus tareas como objetos invocables & Lt; V & Gt; . Pero si por alguna razón desea detectar si la tarea se canceló desde el código invocable & Lt; V & Gt; , simplemente verifique el estado de interrupción del hilo con Thread.interrupted () .

Sugiero escribir un pequeño caso de prueba que te permite llamar a cancel() mientras que su Future ejemplo cuelga en done() y a ver qué pasa.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top