Как мне прочитать результат SwingWorker * без * занятого ожидания? - PullRequest
6 голосов
/ 08 сентября 2011

Я пишу приложение, которое выполняет свои действия с меню файла, используя SwingWorker.Каждый вызываемый метод возвращает значение boolean, которое сообщает, была ли операция успешно выполнена или нет.

В данный момент я использую ожидание результата в состоянии занятости, например:

public boolean executeOperation() {
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
        @Override
        protected Boolean doInBackground() throws Exception {
            // ..

            if (aborted) {
                return false;
            }

            // ..

            return true;
        }
    };

    worker.execute();

    // busy wait
    while (!worker.isDone())
        ;

    try {
        return worker.get().booleanValue();
    } catch (Exception e) {
        // handle exceptions ..
        return false;
    }
}

Есть ли менее интенсивный способ решения этой проблемы?

Непосредственное использование worker.get() не сработает, так как блокирует EDT, ожидая завершения задачи - значениедаже диалоговые окна, которые я открываю из SwingWorker, не будут окрашены.

РЕДАКТИРОВАТЬ: Если возможно, я бы хотел, чтобы метод (или рабочий) сообщал свой результат асинхронно.Я реализую несколько коротких методов (файл -> открыть, новый, закрыть, сохранить, сохранить как, выйти), которые зависят друг от друга (т. Е. Когда попытка выхода, закрытие вызовов, закрытие может вызвать сохранение, сохранение может вызвать сохранениекак).Кажется, что решение этой проблемы асинхронно усложняет код.

Ответы [ 5 ]

6 голосов
/ 08 сентября 2011

Суть SwingWorker заключается в том, чтобы точно запустить некоторую задачу в фоновом режиме и не блокировать EDT. Либо вы хотите что-то синхронное, и EDT будет заблокирован, что бы вы ни пытались, либо вы хотите что-то асинхронное, и фоновая задача должна обновить свой статус, используя метод publish SwingWorker.

Вы можете отобразить блокирующее модальное диалоговое окно с индикатором выполнения во время выполнения задачи и скрыть его после завершения задачи.

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

4 голосов
/ 09 сентября 2011

Один из способов, упомянутых выше несколькими людьми, - это переопределить метод done SwingWorker. Однако, если по какой-либо причине вы хотите опубликовать код SwingWorker вне SwingWorker и в вызывающем коде, вы можете воспользоваться поддержкой изменения свойств SwingWorker. Просто добавьте PropertyChangeListener в SwingWorker и прослушайте свойство состояния с именем свойства «состояние». Затем вы можете извлечь состояние SwingWorker с помощью его метода getState (). Когда это будет сделано, он вернет значение DONE перечисления SwingWorker.StateValue. Например (из ответа, который я дал в другой теме здесь, на SO):

  if (turn == white) {
     try {
        final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() {
           @Override
           protected Move doInBackground() throws Exception {
              Engine e = new Engine(); // Engine is implemented by runnable
              e.start();
              Move m = e.getBestMove(board);                  
              return m;
           }
        };

        mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
           public void propertyChange(PropertyChangeEvent evt) {
              if (StateValue.DONE == mySwingWorker.getState()) {
                 try {
                    Move m = mySwingWorker.get();

                    // TODO: insert code to run on the EDT after move determined

                 } catch (InterruptedException e) {
                    e.printStackTrace();
                 } catch (ExecutionException e) {
                    e.printStackTrace();
                 }
              }
           }
        });
        mySwingWorker.execute();

     } catch (Exception e) {
        e.printStackTrace();
     }
  }
4 голосов
/ 08 сентября 2011

Использование worker.get() напрямую не сработает, так как блокирует EDT, ожидая завершения задачи - то есть даже диалоги, которые я открываю из SwingWorker, не будут окрашены.

Они не соответствуют текущему коду. Ваше занятое ожидание блокирует EDT так же, как и вызов worker.get() - существует только один поток диспетчеризации событий, а диалоги в SwingWorker блокируются так же, если этот поток вращается в цикле или ожидает блокировки. Проблема здесь заключается в том, что если метод выполняется в EDT, он просто не может возвратить значение из асинхронной операции (без включения EDT) своему вызывающему.

Правильный способ реагирования на завершенную асинхронную обработку - это переопределение метода done() в SwingWorker.

Также проверьте http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html для получения дополнительной информации.

4 голосов
/ 08 сентября 2011

Вы можете использовать асинхронную парадигму.Посмотрите на Observer / Observable и используйте задание, чтобы передать результат обратно объекту, который в данный момент выполняет опрос.

0 голосов
/ 28 марта 2013

Я столкнулся с подобной проблемой, когда хотел, чтобы функция возвращала значение, которое будет вычислено в рабочем объекте. Я не хотел просто заставить эту тему блокировать EDT. Я также не хотел, чтобы это блокировалось. Поэтому я использовал такой семафор:

public boolean executeOperation() {
    final Semaphore semaphore = new Semaphore(1);
    semaphore.acquire(1); // surround by try catch...
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
        @Override
        protected Boolean doInBackground() throws Exception {
            // ..

            if (aborted) {
                semaphore.release();
                return false;
            }

            // ..
            semaphore.release();
            return true;
        }
    };

    worker.execute();



    try {
        semaphore.tryAcquire(1, 600, TimeUnit.SECONDS); // awakes when released or when 10 minutes are up.
        return worker.get().booleanValue(); // blocks here if the task doesn't finish in 10 minutes.
    } catch (Exception e) {
        // handle exceptions ..
        return false;
    }
}

Полагаю, это не идеально для всех ситуаций. Но я подумал, что это был альтернативный подход, который был очень полезен для меня.

...