Вопрос

В настоящее время я ищу неприятную ошибку в многопоточной среде, используя FutureTasks и Executors.Основная идея заключается в том, чтобы фиксированное количество потоков выполняло отдельные FutureTask, которые вычисляют результат, который должен отображаться в таблице (не говоря уже о графическом интерфейсе).

Я так долго на это смотрю, что начинаю сомневаться в своем здравом уме.

Рассмотрим этот фрагмент кода:

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

Когда done() вызывается Исполнителем, обрабатывающим экземпляр MyTask, я проверяю, добрался ли я туда, потому что задача была отменена.Если да, то все остальные действия я пропускаю, особенно не звоню sendMessage().

В документации FutureTask.done() говорится:

Защищенный метод вызывается, когда эта задача переходит в состояние isDone (обычно или посредством отмены).Реализация по умолчанию ничего не делает.Подклассы могут переопределять этот метод для вызова обратных вызовов завершения или выполнения бухгалтерского учета.Обратите внимание, что вы можете запросить статус внутри реализации этого метода, чтобы определить, была ли эта задача отменена.(Справочник по API)

Но чего я не получаю из документации FutureTask это семантика пока done() исполняется.Что, если я пройду isCancelled() проверьте вначале, но сразу после этого какой-то другой поток вызывает мой cancel() метод?Заставит ли это мою задачу передумать и ответить isCancelled() == true С тех пор?

Если да, то как я позже узнаю, было ли сообщение отправлено?Смотря на isDone() просто сказал бы мне, что выполнение задачи завершено, но так как isCancelled() были правдой и тогда, я не мог знать, удастся ли отправить сообщение вовремя.

Возможно, это очевидно, но я сейчас этого не вижу.

Это было полезно?

Решение

Из API (выделено мое):

  

публичная логическая отмена (логическое mayInterruptIfRunning)

     

Описание скопировано из интерфейса: будущее

     

Пытается отменить выполнение этого задания. Эта попытка потерпит неудачу, если задача уже выполнена , уже отменена или не может быть отменена по какой-либо другой причине.

Таким образом, FutureTask отрабатывает предположение, что вы не можете отменить задачу, когда она перешла на этап isDone.

Другие советы

< => вызывается не более одного раза для любого данного экземпляра и вызывается только по одной причине - FutureTask#done() завершено с ошибкой или без нее или run() выполнено до того, как произошло какое-либо из предшествующих событий. Запись о завершении любого из этих результатов - latching . Причина, по которой cancel() завершено, не может измениться, независимо от конкурирующих событий, которые, по-видимому, происходят & В то же время. & Quot;

Следовательно, в FutureTask только один из isCancelled() или isDone() вернет true тогда и навсегда. Трудно провести различие между set() сообщением об истинности путем ошибки или успешного завершения. Вы не можете переопределить setException(Throwable) или AQS решительно, так как оба делегируют внутренний get() , чтобы решить, должна ли попытка записать успешное получение значения или возникновение исключения. Переопределение любого метода только дает вам знать, что он был вызван, но вы не можете наблюдать решение, принятое базовой реализацией. Если происходит какое-либо событие & Quot; слишком поздно & Quot; & # 8212; скажем, после отмены & # 8212; попытка записи значения или исключения будет игнорируются.

Изучая реализацию, я вижу единственный способ отличить неотмененный успешный результат ошибки - прикусить пулю и вызвать <=>.

Почему бы не отправить сообщение «за пределы» задачи, основываясь на результатах Будущее<V> объект, возвращаемый ИсполнительСервис?Я использовал этот шаблон, и он, кажется, работает хорошо:Отправьте кучу Вызываемый<V> задачи через ИсполнительСервис.Затем для каждой основной задачи отправьте дополнительную задачу, ожидающую выполнения Будущее<V> основной задачи и выполняет некоторые последующие действия (например, отправляет сообщение), только если Будущее<V> указывает на то, что основная задача выполнена успешно.При таком подходе нет никаких догадок.Когда звонок в Будущее<V>.get() возвращается, вам гарантировано, что задача достигла конечного состояния, пока вы не вызываете версию получать для этого требуется аргумент тайм-аута.

Если вы воспользуетесь этим подходом, вам следует использовать два отдельных ИсполнительСервис экземпляры:один для основных задач и один для второстепенных.Это необходимо для предотвращения взаимоблокировок.Вы не хотите, чтобы второстепенные задачи запускались и потенциально блокировали запуск первичных задач, когда размер пула потоков ограничен.

Нет необходимости продлевать БудущаяЗадача<V> совсем.Просто реализуйте свои задачи как Вызываемый<V> объекты.Но если по какой-то причине вы хотите определить, была ли задача отменена изнутри Вызываемый<V> код, просто проверьте статус прерывания потока с помощью Поток.прерванный().

Я предлагаю написать небольшой тестовый пример, который позволит вам вызывать cancel(), пока ваш экземпляр Future висит в done(), и посмотреть, что произойдет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top