2013-02-27 79 views
0

在编写计算量大的应用程序时,我尝试使用SwingWorker类将负载分散到多个CPU核心。然而,这个类别的行为证明有点奇怪:似乎只有一个核心被利用。如何正确地扩展FutureTask

当搜索互联网,我发现了一个很好的回答这个网站上(见Swingworker instances not running concurrently,通过user268396答案),这 - 除了问题的原因 - 也提到了一个可能的解决方案:

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

作为一名Java初学者,我不完全确定如何正确地做到这一点。不仅需要将一些初始数据传递给FutureTask对象,还需要像SwingWorker一样返回结果。因此,任何示例代码都将非常感谢。

NVX

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

在执行FutureTask that implements Callable中提到的简单而优雅的解决方案后,出现了另一个问题。如果我使用ExecutorService来创建单独的线程,在线程完成运行后如何执行特定的代码?我试图覆盖FutureTask对象的完成()(请参阅下面的代码),但我想应该在应用程序的事件分派线程中完成“显示结果”位(或任何与GUI相关的东西) (美东时间)。因此:如何将可运行代码提交给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()); 
      } 
     } 
    } 
} 
+0

检查是否有帮助。检查[这里] [1] [1]:http://stackoverflow.com/questions/1808776/extending-futuretask-how-to-handle-cancel – chiru 2013-02-27 13:21:39

+0

谢谢你,我会看它。在此期间,我在这里找到了[主题](http://stackoverflow.com/questions/1468598/futuretask-that-implements-callable),这似乎是实现我所需要的另一种方式(即使我需要创建一个额外的抽象类)。 – nvx 2013-02-27 14:31:44

回答

2

几个百分点:

  • 你很少需要使用FutureTask直接,只需要实现可赎回或Runnable接口和提交的实例提供给Executor
  • ,以更新的图形用户界面,当你完成后,在最后一步的run()/call()方法,使用SwingUtilities.invokeLater()与代码更新用户界面。

注意,您可以仍然使用SwingWorker,公正,而不是调用​​,提交的SwingWorker你的执行人来代替。

如果你需要处理所有结果放在一起时,所有的线程完成更新GUI之前,那么我建议:

  • 每个工人藏匿它的结果变成一个线程安全的,共享的列表
  • 最后工人结果添加到列表中,则应该做的后处理工作
  • 该做的后处理工作,那么应该调用SwingUtilities.invokeLater()与最终结果的工人
+0

谢谢,我会尝试.invokeLater()方法。至于将'SwingWorker'提交给'ExecutorService',我试过了,结果仅仅使用了一个CPU核心(准确地说:所有核心之间的负载是平衡的,但实际的负载相当于一个核心 - - 例如,对于八核CPU大约为12%)。 – nvx 2013-02-28 13:47:04

+0

@nvx - 那么你的执行者服务可能做错了什么,或者你的工作不足以使用多个核心。 – jtahlborn 2013-02-28 13:52:35

+0

自昨天以来,我一直在用'SwingUtilities.invokeLater()'来修补,但是 - 即使它确实有效 - 它也不是一个理想的解决方案。原因是我被迫做结果后处理_before_他们返回到主线程是不幸的,因为我绝对需要为所有工作线程的结果做到这一点。最初,我想实现'FutureTask's,因为它们能够在任务完成后执行代码。像这样可以直接通过'ExecutorService'对象? – nvx 2013-03-01 08:15:03

2

我试图让使用SwingWorker类的负载蔓延到 多个CPU内核。然而,这个类的行为证明是 有点奇怪:似乎只有一个核心被利用。