2

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?

4

3 回答 3

0

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

更新:

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

于 2010-05-08T14:51:03.127 回答
0

A1+A2。Yatendra,您的 Main 是否SwingWorker必须是唯一将中期结果传递给 的人EDT?如果您的任务也是SwingWorker实例,那么 Main Worker 可以将发送临时结果的责任委托EDT给它们,并只负责TaskWorkers生命周期。

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您的项目需要另一个来安排您的任务执行,我将实现一个类似的发布流程机制来执行您的 Main Swing 工作线程和该任务线程之间的通信。尽管它似乎是重复的,但您可以使用 ajava.concurrent.ConcurrentQueue来存储可用的临时结果吗?

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

于 2010-06-27T15:50:11.983 回答
-1

SwingWorker 也是一个 Future。因此,它具有 get() 方法,可以在 done() 方法中使用该方法以在该方法完成时获取 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.
    }
  }
};
于 2010-05-08T15:24:24.987 回答