2010-08-19 3 views
6

У меня есть SwingWorker, который вызывает некоторый код, который не проверяет прерывание потока. После вызова worker.cancel(true) метод worker.get() сразу же отправит CancellationException (как предполагается). Однако, поскольку код фоновой задачи никогда не проверяет прерывание потока, он с радостью продолжает выполнение.Ожидание отложенного будущего на самом деле завершено

Есть ли стандартный способ подождать фоновой задачи до на самом деле закончить? Я хочу показать сообщение «Отменить ...» или что-то вроде этого и заблокировать до тех пор, пока задача не завершится. (Я уверен, что я всегда мог бы выполнить это с флагом в рабочем классе, если это необходимо, просто для поиска любых других решений.)

ответ

3

Я немного поиграл с этим, и вот что я придумал. Я использую CountDownLatch и в основном подвергая его методу await() как метод на моем объекте SwingWorker. Все еще ищут лучшие решения.

final class Worker extends SwingWorker<Void, Void> { 

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1); 

    @Override 
    protected Void doInBackground() throws Exception { 
     try { 
      System.out.println("Long Task Started"); 

      /* Simulate long running method */ 
      for (int i = 0; i < 1000000000; i++) { 
       double d = Math.sqrt(i); 
      } 

      return null; 
     } finally { 
      actuallyFinishedLatch.countDown(); 
     } 
    } 

    public void awaitActualCompletion() throws InterruptedException { 
     actuallyFinishedLatch.await(); 
    } 

    public static void main(String[] args) { 
     Worker worker = new Worker(); 
     worker.execute(); 

     try { 
      TimeUnit.SECONDS.sleep(1); 
     } catch (InterruptedException e) { 

     } 

     System.out.println("Cancelling"); 
     worker.cancel(true); 

     try { 
      worker.get(); 
     } catch (CancellationException e) { 
      System.out.println("CancellationException properly thrown"); 
     } catch (InterruptedException e) { 

     } catch (ExecutionException e) { 

     } 

     System.out.println("Awaiting Actual Completion"); 
     try { 
      worker.awaitActualCompletion(); 
      System.out.println("Done"); 
     } catch (InterruptedException e) { 

     } 
    } 

} 
1

. Ближайшее к стандартному или готовому способу это progress свойство и/или пара методов публикации/процесса, предоставленные SwingWorker. Вы можете установить это значение «Я закончил» в конце метода, чтобы указать, что фоновая работа выполнена. Поток, ожидающий рабочего качателя, может вывести сообщение «Отмена ...» и периодически проверять ход выполнения, чтобы убедиться, что он достиг завершения. Если ожидающий поток - это качающийся EDT, вам нужно будет использовать таймер, чтобы периодически проверять свойство прогресса и очищать сообщение об отмене при завершении.

Вот пример кода, который работает упорную фоновый поток, который будет отменен, а затем ждет, пока прогресс не достигнет 100.

@Test 
public void testSwingWorker() 
{ 
    SwingWorker worker = new SwingWorker() { 

     @Override 
     protected void process(List chunks) 
     { 
      for (Object chunk : chunks) 
      { 
       System.out.println("process: "+chunk.toString()); 
      } 
     } 

     @Override 
     protected void done() 
     { 
      System.out.println("done"); 
     } 

     @Override 
     protected Object doInBackground() throws Exception 
     { 
      // simulate long running method 
      for (int i=0; i<1000000000; i++) 
      { 
       double d = Math.sqrt(i); 
      } 
      System.err.println("finished"); 
      publish("finished"); 
      setProgress(100); 
      return null; 
     } 
    }; 
    Thread t = new Thread(worker); 
    t.start(); 

    try 
    { 
     worker.get(1, TimeUnit.SECONDS); 
    } 
    catch (InterruptedException e)  { 
    } 
    catch (ExecutionException e)  { 
    } 
    catch (TimeoutException e)  { 
    } 

    worker.cancel(true); 

    // now wait for finish. 
    int progress = 0; 
    do 
    { 
     try 
     { 
      Thread.sleep(1000); 
     } 
     catch (InterruptedException e) 
     { 
     } 
     progress = worker.getProgress(); 
     System.out.println(String.format("progress %d", progress)); 
    } 
    while (progress<100); 
} 

Альтернативный подход заключается в использовании publish\process пар метод протолкнуть особое значение указывая, что фоновый поток завершен в EDT. Ваш метод переопределения process в SwingWorker затем подбирает это специальное значение и скрывает сообщение «Отмена ...». Преимущество этого заключается в том, что не требуется опрос или таймеры. Код примера показывает, что хотя done вызывается, как только задача отменяется, пары метода публикации/процесса все еще работают, даже когда задача отменяется.

+0

Обе эти идеи вещи, которые я до сих пор не считается , Мне не нравится использовать прогресс по той же причине, которую вы указали, - это требует опроса. Мне нравится идея публикации/процесса по большей части, но она связывает логику, которую я хочу сделать, когда задача _actually_ закончена для самого класса SwingWorker. По той же причине я редко использую метод «done()» и вместо этого вместо этого добавляю слушателей к рабочему объекту. –

+0

Я согласен с вами, опубликовать/обрабатывать лучше всех. Что касается соединения, вы можете отделить его, создав подкласс SwingWorker, который переопределяет метод процесса, прослушивает значение «Я сделан» и уведомляет зарегистрированных слушателей о том, что фоновая задача действительно выполнена. Это отделяет логику от рабочего качания. – mdma

+0

Правда, но как же тогда объект прослушивания будет блокироваться, пока не получит уведомление? –

1

Вдохновленные решением Пола Благословения я улучшил его немного, чтобы стать классом, вы можете унаследовать, чтобы получить желаемый funcitonality:

class AwaitingWorker<T,V> extends SwingWorker<T, V> { 

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1); 

    /** 
    * Override this to do something useful 
    */ 
    protected abstract void performOperation(); 

    @Override 
    protected final T doInBackground() throws Exception { 
     try { 
      return performOperation(); 
     } finally { 
      actuallyFinishedLatch.countDown(); 
     } 
    } 

    public void awaitActualCompletion() throws InterruptedException { 
     actuallyFinishedLatch.await(); 
    } 

} 
+0

Я предполагаю, что done() теперь вызывает awaitActualCompletion(), но теперь done() может быть долго выполняемой задачей, поскольку он ждет ожидания ожидания doInBackground() для завершения после отмены с отменой(). Это, что собирается заблокировать EventDispatchThread –

+0

Нельзя ли отменить задачу, которая запланирована на выполнение, но на самом деле не запущена? Кажется, что awaitActualCompletion должен как-то проверить, что задача не была отменена до ее запуска до вызова. – Ryan

Смежные вопросы