Pergunta

Atualmente, estou a caça de um bug desagradável em um ambiente multi-threaded usando FutureTasks e Executores.A idéia básica é que isso tem um número fixo de linhas de execução individual FutureTasks que calcular um resultado que está a ser exibido em uma tabela (não importa o GUI aspecto aqui).

Eu tenho estado a olhar para isso por tanto tempo, que eu estou começando a duvidar da minha sanidade.

Considere este trecho 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
         ... 
      }
   }
   ...
}

Quando done() é chamado por um Executor de manipulação de uma instância de MyTask, eu verificar se lá cheguei, porque a tarefa foi cancelada.Se for assim, eu ignorar todos os demais atividades, especialmente as que eu não chame sendMessage().

A documentação para FutureTask.feito() diz:

Protegido método invocado quando esta tarefa transições para o estado isDone (se normalmente ou através de cancelamento).A implementação padrão não faz nada.Subclasses pode substituir este método para invocar chamadas de retorno de conclusão ou executar a escrituração.Note que você pode consultar o status dentro da implementação deste método para determinar se esta tarefa foi cancelada.(Referência de API)

Mas o que eu não entendo da documentação de FutureTask são a semântica enquanto done() está sendo executado.Que se eu passar o isCancelled() verifique no início, mas logo depois que alguma outra thread chama o meu cancel() método?Será que fazer minha tarefa de mudar a sua mente e responder a isCancelled() == true a partir de então?

Se sim, como seria mais tarde eu saber se a mensagem foi enviada?Olhando para isDone() poderia apenas me dizer que a execução da tarefa foi concluída, mas como isCancelled() eram verdadeiras, então, assim, eu não poderia saber se ele tem para enviar a mensagem em tempo.

Talvez isso é óbvio, mas eu realmente não vê-lo agora.

Foi útil?

Solução

A partir da API (ênfase minha):

public boolean cancelar(boolean mayInterruptIfRunning)

Descrição copiados a partir da interface:Futuro

Tentativas para cancelar a execução desta tarefa. Essa tentativa falhará se a tarefa já concluída, que já foi cancelada, ou não pôde ser cancelada por algum outro motivo.

Assim FutureTask está trabalhando com o pressuposto de que você não pode cancelar uma tarefa quando ele foi transferido para o isDone fase.

Outras dicas

FutureTask#done() não é mais do que uma vez para uma determinada instância chamada, e isso só é chamado por uma razão - run() concluída com ou sem erro, ou cancel() correu antes de qualquer um dos eventos anteriores ocorreram. O registro de conclusão por qualquer um desses resultados é travamento . A razão de um FutureTask concluída não pode mudar, independentemente de eventos aparentemente acontecendo concorrentes "ao mesmo tempo."

Assim, dentro FutureTask#done() apenas um dos isCancelled() ou isDone() retornará true, em seguida, e para sempre. É difícil distinguir entre isDone() relatórios verdade por meio de erro ou conclusão bem-sucedida. Você não pode substituir set() ou setException(Throwable) decisivamente, tanto como delegado para o interior AQS para decidir se o tentativa para gravar um sucesso produzindo de um valor ou encontrando uma exceção deve ficar. Substituindo o método só permite que você sabe que ele foi chamado, mas você não pode observar a decisão tomada pela implementação base. Se qualquer evento ocorrer "tarde demais" -Diga, após cancelamento-a tentativa de gravar o valor ou a exceção será ignorada.

Estudar a implementação, a única maneira que vejo para discernir um resultado não-canceladas sucesso de um erro é que morder a bala e chamada get().

Por que não enviar a mensagem "fora" da tarefa, com base no resultado da Future objeto retornado por um ExecutorService ? Eu usei esse padrão e parece funcionar bem: Enviar um monte de Callable tarefas através de um ExecutorService . Então, para cada tarefa principal, enviar uma tarefa secundária que espera na Future da tarefa primária e faz alguma acção de acompanhamento (como enviar uma mensagem) somente se o Future indica que a principal tarefa concluída com êxito. Não há nenhuma adivinhação com esta abordagem. Quando a chamada para Future .get () retornos, você tem a garantia de que a tarefa atingiu um estado terminal, contanto que você não chamar a versão de get que leva um argumento de tempo limite.

Se você tomar essa abordagem, você deve usar dois separados ExecutorService casos: um para as tarefas principais e outra para as secundárias. Isso é para evitar impasses. Você não quer tarefas secundárias para iniciar e potencialmente bloquear tarefas principais de começar quando o tamanho do pool de threads é limitado.

Não há necessidade de estender FutureTask em tudo. Apenas implementar suas tarefas como Callable objetos. Mas se por algum motivo você deseja detectar se a tarefa foi cancelada de dentro do Callable código, basta verificar o estado de interrupção da linha com Thread.interrupted () .

Eu sugiro para escrever um caso de teste pequeno que lhe permite chamar cancel() enquanto seus Future exemplo trava em done() e ver o que acontece.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top