2013-02-27 118 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內核。然而,這個類的行爲證明是 有點奇怪:似乎只有一個核心被利用。