Java-SwingWorker-问题

发布于 2024-08-31 13:59:41 字数 798 浏览 9 评论 0原文

我正在开发一个 Java 桌面应用程序。此应用程序执行相同的任务 public class MyTask Implements Callable{ 同时在多个线程中。

现在,当用户单击“开始”按钮时,我创建了一个 SwingWorker myWorker 并执行了它。

现在,此 myWorker 创建 MyTask 的多个实例,并将它们提交给 ExecutorService

每个 MyTask 实例都有一个循环,并在每次迭代时生成中间结果。现在,我想在生成每个 MyTask 实例后立即收集这些中间结果。然后,从每个 MyTask 实例收集这些中间结果后,我想通过 SwingWorker.publish(MyObject) 发布它,以便在 EDT 上显示进度。

Q1.我该如何实现这个?我是否应该创建 SwingWorkerMyTask 子类而不是 Callable 来获得中间结果,因为我认为 Callable 仅返回最终结果。

Q2。如果回答Q1。是的,那么您能给我一个小例子来展示如何获取这些中间结果并聚合它们,然后从主 SwingWorker 发布它们吗?

Q3.如果在这种情况下我无法使用SwingWorker,那么我该如何实现呢?

I am developing a Java Desktop Application. This app executes the same task public class MyTask implements Callable<MyObject> { in multiple thread simultaneously.

Now, when a user clicks on a "start" button, I have created a SwingWorker myWorker and have executed it.

Now, this myWorker creates multiple instances of MyTask and submits them to an ExecutorService.

Each MyTask instance has a loop and generates an intermediate result at every iteration. Now, I want to collect these intermediate results from each MyTask instances as soon as they are generated. Then after collecting these intermediate results from every MyTask instance, I want to publish it through SwingWorker.publish(MyObject) so that the progress is shown on the EDT.

Q1. How can I implement this? Should I make MyTask subclass of SwingWorker instead of Callable to get intermediate results also, because I think that Callable only returns final result.

Q2. If the answer of Q1. is yes, then can you give me a small example to show how can I get those intermediate results and aggregate them and then publish them from main SwingWorker?

Q3. If I can't use SwingWorker in this situation, then how can I implement this?

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

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

发布评论

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

评论(3

绅刃 2024-09-07 13:59:41

看一下 ExecutorCompletionService。它是一个 Executor,提供 take 方法来检索任何已完成任务的结果。

更新:

扩展 SwingWorker 不会执行您想要的操作,因为它专门用于将工作从 EDT 卸载到后台线程。您不能使用它将工作从后台线程卸载到其他后台线程。调用 SwingWorker.publish 会产生相当于 SwingUtilities.invokeLater 的结果。据我所知,没有任何机制可以从后台线程到后台线程执行相同的操作。最好的选择是使用对 Queue 的引用来创建 MyTask ,并让 SwingWorker.doInBackground 轮询队列以获取中间结果。

Take a look at ExecutorCompletionService<T>. It's an Executor that supplies a take method to retrieve the result of any completed task.

Update:

Extending SwingWorker will not do what you want as it is specifically intended for offloading work from the EDT to a background thread. You can't use it to offload work from a background thread to other background threads. Calls to SwingWorker.publish result in the equivalent of a SwingUtilities.invokeLater. There is no mechanism I am aware of for doing the same thing from a background thread to a background thread. Your best bet is to create your MyTask with a reference to a Queue and have your SwingWorker.doInBackground poll the queue for intermediate results.

同展鸳鸯锦 2024-09-07 13:59:41

A1+A2
Yatendra,您的 Main SwingWorker 是否必须是唯一将临时结果传递给 EDT 的?如果您的任务也是 SwingWorker 实例,则 Main Worker 可以将将临时结果发送回 EDT 的责任委托给它们,而只需处理 TaskWorkers< /code> 生命周期。

package threading;

import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

class MainSwingWorker extends SwingWorker<Void, Void> {
    private List<TaskWorker> tasks;

    public MainSwingWorker() {
        tasks = new LinkedList<TaskWorker>();
        for(int i=0; i<2; i++) 
            tasks.add(new TaskWorker(i));
    }

    @Override
    public Void doInBackground() throws Exception {
        Test.log("Building tasks.");                    
        for(TaskWorker task : tasks) 
            launch(task);
        Test.log("Waiting 5 secs.");
        Thread.sleep(5000);

        Test.log("Cancelling tasks");

        for(TaskWorker task : tasks ) 
            task.cancel(true);

        return null;
    }

    private void launch(final TaskWorker task) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Test.log("Launching task worker.");
                task.execute();
            }
        });     
    }
}

class TaskWorker extends SwingWorker<Void, String> {
    private int id;

    public TaskWorker(int wid) {
        id = wid;
    }

    @Override
    public Void doInBackground() throws Exception {     
        System.out.format("[%s] Starting worker %s\n", Thread.currentThread().getName(), id );
        while( !isCancelled() ) {
            // ***************************
            // your task process code here
            // ***************************
            publish(String.format("A dummy interim result #%s", id));
            Thread.sleep(1000);
        }       
        return null;
    }

    @Override
    public void process(List<String> results) {
        // it's pretty obvious, that once this method gets called you can safely 
        // call the Swing API from EDT among with the interim results
        for(String result : results )
            Test.log(result);
    }
}

public class Test {

    public static void log(String msg) {
        System.out.format("[%s] %s\n", Thread.currentThread().getName(), msg);
    }

    public static void main(String[] args) throws Exception {
        log("Init.");
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                log("Starting main worker.");
                MainSwingWorker worker = new MainSwingWorker();
                worker.execute();                           
            }
        });
        Thread.sleep(7000);
        log("Finished.");
    }
}

请记住,这只是一个测试,我知道有一些丑陋的 Thread.sleep(long) 调用。

[main] Init.
[AWT-EventQueue-0] Starting main worker.
[SwingWorker-pool-1-thread-1] Building tasks.
[SwingWorker-pool-1-thread-1] Waiting 5 secs.
[AWT-EventQueue-0] Launching task worker.
[AWT-EventQueue-0] Launching task worker.
[SwingWorker-pool-1-thread-2] Starting worker 0
[SwingWorker-pool-1-thread-3] Starting worker 1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[SwingWorker-pool-1-thread-1] Cancelling tasks
[main] Finished.

A3
但是,如果您的项目需要另一个 ExecutorService 来安排任务执行,那么我将实现类似的发布进程机制来执行主 Swing 工作线程和该任务线程之间的通信。虽然看起来很重复,但您可以使用 java.concurrent.ConcurrentQueue 来存储可用的临时结果吗?

PS:我几天前才注意到,但是 SwingWorkers 周围有一个恼人的错误,阻止其 ExecutorService 缓存未使用的线程

A1+A2.
Yatendra, is it necessary that your Main SwingWorker must be the only one that passes interim results to the EDT? If your tasks were also SwingWorker instances, the Main Worker could delegate the responsability of sending interim results back to the EDT to them and just take care of the TaskWorkers life-cycle.

package threading;

import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

class MainSwingWorker extends SwingWorker<Void, Void> {
    private List<TaskWorker> tasks;

    public MainSwingWorker() {
        tasks = new LinkedList<TaskWorker>();
        for(int i=0; i<2; i++) 
            tasks.add(new TaskWorker(i));
    }

    @Override
    public Void doInBackground() throws Exception {
        Test.log("Building tasks.");                    
        for(TaskWorker task : tasks) 
            launch(task);
        Test.log("Waiting 5 secs.");
        Thread.sleep(5000);

        Test.log("Cancelling tasks");

        for(TaskWorker task : tasks ) 
            task.cancel(true);

        return null;
    }

    private void launch(final TaskWorker task) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Test.log("Launching task worker.");
                task.execute();
            }
        });     
    }
}

class TaskWorker extends SwingWorker<Void, String> {
    private int id;

    public TaskWorker(int wid) {
        id = wid;
    }

    @Override
    public Void doInBackground() throws Exception {     
        System.out.format("[%s] Starting worker %s\n", Thread.currentThread().getName(), id );
        while( !isCancelled() ) {
            // ***************************
            // your task process code here
            // ***************************
            publish(String.format("A dummy interim result #%s", id));
            Thread.sleep(1000);
        }       
        return null;
    }

    @Override
    public void process(List<String> results) {
        // it's pretty obvious, that once this method gets called you can safely 
        // call the Swing API from EDT among with the interim results
        for(String result : results )
            Test.log(result);
    }
}

public class Test {

    public static void log(String msg) {
        System.out.format("[%s] %s\n", Thread.currentThread().getName(), msg);
    }

    public static void main(String[] args) throws Exception {
        log("Init.");
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                log("Starting main worker.");
                MainSwingWorker worker = new MainSwingWorker();
                worker.execute();                           
            }
        });
        Thread.sleep(7000);
        log("Finished.");
    }
}

Keep mind that this is just a test, I know that there are a few ugly Thread.sleep(long) calls.

[main] Init.
[AWT-EventQueue-0] Starting main worker.
[SwingWorker-pool-1-thread-1] Building tasks.
[SwingWorker-pool-1-thread-1] Waiting 5 secs.
[AWT-EventQueue-0] Launching task worker.
[AWT-EventQueue-0] Launching task worker.
[SwingWorker-pool-1-thread-2] Starting worker 0
[SwingWorker-pool-1-thread-3] Starting worker 1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[AWT-EventQueue-0] A dummy interim result #0
[AWT-EventQueue-0] A dummy interim result #1
[SwingWorker-pool-1-thread-1] Cancelling tasks
[main] Finished.

A3
But if having another ExecutorService to schedule your task execution is a requirement in your project, I would implement a similar publish-process mechanism to perform communication between your Main Swing Worker Thread and that Task Thread. Although it seems to be repetitive, You may use a java.concurrent.ConcurrentQueue to store interim results as they become available?

PS: I just noticed a few days ago, but there is an annoying bug around SwingWorkers that prevents its ExecutorService from caching unused threads.

夜无邪 2024-09-07 13:59:41

SwingWorker 也是未来。因此,它具有 get() 方法,可以在 did() 方法内部使用该方法,以在该方法完成时获取 doInBackground() 的结果。

因此,结构变得有点像:

SwingWorker<T,P> sw=new SwingWorker<T,P>() {

  @Override
  public T doInBackground() throws Exception {
    T result;
    // do stuff here
    return result;
  }

  @Override
  public void done() {
    try {
      T result=get();
      // do stuff with result.
    }
    catch(ExecutionException e) {
      Exception fromDoInBackground= (Exception) e.getCause();
      // handle exception thrown from doInBackground()
    }
    catch(InterruptedException i) {
      // handle the case in which a SwingWorker was cancelled. typically: do nothing.
    }
  }
};

A SwingWorker is also a Future. As such it has the get() method which can be used inside the done() method to get the result of doInBackground() when that method finishes.

Thus the construct becomes somewhat like:

SwingWorker<T,P> sw=new SwingWorker<T,P>() {

  @Override
  public T doInBackground() throws Exception {
    T result;
    // do stuff here
    return result;
  }

  @Override
  public void done() {
    try {
      T result=get();
      // do stuff with result.
    }
    catch(ExecutionException e) {
      Exception fromDoInBackground= (Exception) e.getCause();
      // handle exception thrown from doInBackground()
    }
    catch(InterruptedException i) {
      // handle the case in which a SwingWorker was cancelled. typically: do nothing.
    }
  }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文