等待取消的未来真正完成

发布于 2024-09-15 06:47:31 字数 313 浏览 1 评论 0原文

我有一个 SwingWorker,它调用一些不检查线程中断的代码。调用 worker.cancel(true) 后,worker.get() 方法将立即抛出 CancellationException (正如预期的那样) 。但是,由于后台任务的代码从不检查其线程是否被中断,因此它会愉快地继续执行。

是否有等待后台任务实际完成的标准方法?我希望显示“正在取消...”消息或类似的消息并阻止,直到任务终止。 (我确信如果有必要,我总是可以在工人类中使用标志来完成此任务,只需寻找任何其他解决方案即可。)

I have a SwingWorker which calls some code that does not check for thread interruption. After the call to worker.cancel(true), the worker.get() method will throw CancellationException immediately (as it is supposed to). However, since the background task's code never checks for its thread to be interrupted, it happily continues executing.

Is there a standard way to wait for the background task to actually finish? I'm looking to show a "Cancelling..." message or something of the sort and block until the task has terminated. (I'm sure I could always accomplish this with a flag in the worker class if necessary, just looking for any other solutions.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你是年少的欢喜 2024-09-22 06:47:31

我对此进行了一些尝试,这就是我的想法。我使用 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) {

        }
    }

}

I played around with this a bit and here's what I came up with. I'm using a CountDownLatch and basically exposing its await() method as a method on my SwingWorker object. Still looking for any better solutions.

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) {

        }
    }

}
丶视觉 2024-09-22 06:47:31

最接近标准或现成方法的是 progress 属性和/或由 SwingWorker 提供的发布/处理方法对。您可以在方法末尾将其设置为“我已完成”值,以指示后台工作已完成。等待 Swing Worker 的线程可以发出“正在取消...”消息,并定期检查进度以查看是否已完成。如果等待线程是 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。然后,SwingWorker 中的 process 重写方法会获取此特殊值并隐藏“正在取消...”消息。这样做的优点是不需要轮询或计时器。示例代码显示,尽管任务取消后立即调用 done,但即使任务取消,发布/处理方法对仍然可以工作。

The closest thing to a standard, or ready-made way of doing this is the progress property and/or the publish/process method pair provided by SwingWorker. You can set this to a "I'm finished" value at the end of the method to indicate the background work is done. The thread waiting on the swing worker can put up a "Canceling..." message and periodically check the progress to see if it has reached completion. If the waiting thread is the swing EDT, then you will need to use a Timer to periodically check the progress property and clear the cancel message when done.

Here's some example code that runs a stubborn background thread, which is canceled, and then waits until the progress reaches 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);
}

An alternative approach is to use the publish\process method pairs to push a special value indicating that the background thread has finished into the EDT. Your process override method in SwingWorker then picks up this special value and hides the "Canceling..." message. The advantage with this is that no polling or timers are needed. The example code shows that although done is called as soon as the task is canceled, the publish/process method pairs still work even when the task is cancelled.

污味仙女 2024-09-22 06:47:31

受到 Paul Blessing 解决方案的启发,我对其进行了一些改进,使其成为一个类,您可以子类化以获得所需的功能:

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

}

Inspired by the solution by Paul Blessing I improved it a little to become a class, you can subclass to get the desired 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();
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文