0

在编写计算量大的应用程序时,我尝试使用 SwingWorker 类将负载分散到多个 CPU 内核。然而,这个类的行为被证明有些奇怪:似乎只使用了一个核心。

在搜索互联网时,我在这个网络上找到了一个很好的答案(参见Swingworker instances not running concurrently,user268396 的回答)——除了问题的原因——还提到了一个可能的解决方案:

为了解决这个问题,你可以做的是使用 ExecutorService 并在其上发布 FutureTasks。这些将提供 99% 的 SwingWorker API(SwingWorker 是 FutureTask 的衍生产品),您所要做的就是正确设置您的 Executor。

作为一名 Java 初学者,我不完全确定如何正确执行此操作。我不仅需要将一些初始数据传递给 FutureTask 对象,还需要像使用 SwingWorker 一样返回结果。因此,任何示例代码都将不胜感激。

nvx

==================== 编辑====================

在实现了FutureTask 中提到的实现 Callable的简单而优雅的解决方案之后,又出现了另一个问题。如果我使用 anExecutorService创建单个线程,如何在线程完成运行后执行特定代码?

我试图覆盖FutureTask对象的 done()(参见下面的代码),但我猜想“显示结果”位(或任何与 GUI 相关的东西)应该在应用程序的事件调度线程 (EDT) 中完成。因此:如何将 runnable 提交给 EDT?

package multicoretest;

import java.util.concurrent.*;

public class MultiCoreTest {

    static int coresToBeUsed = 4;
    static Future[] futures = new Future[coresToBeUsed];

    public static void main(String[] args) {
        ExecutorService execSvc = Executors.newFixedThreadPool(coresToBeUsed);
        for (int i = 0; i < coresToBeUsed; i++) {
            futures[i] = execSvc.submit(new Worker(i));
        }
        execSvc.shutdown();

        // I do not want to block the thread (so that users can
        // e.g. terminate the computation via GUI)
        //execSvc.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

    }

    static class Worker implements Callable<String> {

        private final FutureTask<String> futureTask;
        private final int workerIdx;

        public Worker(int idx) {
            workerIdx = idx;
            futureTask = new FutureTask<String>(this) {
                @Override
                protected void done() {
                    Runnable r = new Runnable() {
                        @Override
                        public void run() {
                            showResults(workerIdx);
                        }
                    };

                    r.run(); // Does not work => how do I submit the runnable
                             // to the application's event dispatch thread?

                }
            };
        }

        @Override
        public String call() throws Exception {
            String s = "";
            for (int i = 0; i < 2e4; i++) {
                s += String.valueOf(i) + " ";
            }
            return s;
        }

        final String get() throws InterruptedException, ExecutionException {
            return futureTask.get();
        }

        void showResults(int idx) {
            try {
                System.out.println("Worker " + idx + ":" +
                        (String)futures[idx].get());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }
    }
}
4

2 回答 2

2

I tried to make use of the SwingWorker class to spread the load to multiple CPU cores. However, behaviour of this class proved to be somewhat strange: only one core seemed to be utilized.

于 2013-02-27T13:10:22.560 回答
2

几点:

  • 您很少需要直接使用 FutureTask,只需实现 Callable 或 Runnable 并将实例提交给 Executor
  • 为了在完成后更新 gui,作为run()/call()方法的最后一步,使用SwingUtilities.invokeLater()代码更新 ui。

请注意,您仍然可以使用SwingWorker, 而不是调用execute(),而是将 SwingWorker 提交给您的 Executor。

如果您需要在更新 gui 之前完成所有线程后一起处理所有结果,那么我建议:

  • 让每个工作人员将其结果存储到一个线程安全的共享列表中
  • 最后一个将结果添加到列表中的工作人员应该进行后处理工作
  • 然后,进行后处理工作的工作人员应调用SwingUtilities.invokeLater()最终结果
于 2013-02-28T13:35:14.883 回答