В ожидании отмененного будущего, чтобы фактически закончить - PullRequest
6 голосов
/ 19 августа 2010

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

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

Ответы [ 3 ]

3 голосов
/ 19 августа 2010

Я немного поиграл с этим, и вот что я придумал.Я использую 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 голос
/ 16 июня 2011

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

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();
    }

}
1 голос
/ 19 августа 2010

Наиболее близким к стандартному или готовому способу сделать это является свойство progress и / или пара методов публикации / процесса, предоставляемая SwingWorker. Вы можете установить значение «Я закончил» в конце метода, чтобы указать, что фоновая работа выполнена. Поток, ожидающий на рабочем объекте Swing, может выдать сообщение «Отмена ...» и периодически проверять прогресс, чтобы увидеть, достиг ли он завершения. Если ожидающим потоком является колеблющийся 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 вызывается, как только задача отменяется, пары методов публикации / процесса все еще работают, даже когда задача отменяется.

...