2011-03-30 103 views
7

我正在使用jsr166y ForkJoinPool在線程之間分配計算任務。但我顯然一定會做錯事。ForkJoinPool並行度= 1死鎖

如果我創建並行度> 1(缺省值爲Runtime.availableProcessors();我已使用2-8個線程運行)的ForkJoinPool,我的任務看起來完美無缺。但是,如果我創建並行度= 1的ForkJoinPool,則會在無法預測的迭代次數後看到死鎖。

是 - 設置並行度= 1是一種奇怪的做法。在這種情況下,隨着線程數量的增加,我正在分析並行算法,並且我想將並行版本(與單個線程一起運行)與基準串行實現進行比較,以便準確確定並行實現的開銷。

下面是一個簡單的例子,說明我看到的問題。 '任務'是對固定數組的虛擬迭代,遞歸地分爲16個子任務。

如果在THREADS = 2(或更多)的情況下運行,它可以可靠地運行到完成,但是如果在THREADS = 1的情況下運行,它總是會死鎖。在不可預知的迭代次數後,主循環掛在ForkJoinPool.invoke()中,等待task.join(),並且工作線程退出。

我與JDK 1.6.0_21和1.6.0_22運行在Linux下,使用版本jsr166y的下載前幾天Doug Lea的網站(http://gee.cs.oswego.edu/dl/concurrency-interest/index.html

對我失去了我的任何建議?提前謝謝了。

package concurrent; 

import jsr166y.ForkJoinPool; 
import jsr166y.RecursiveAction; 

public class TestFjDeadlock { 

    private final static int[] intArray = new int[256 * 1024]; 
    private final static float[] floatArray = new float[256 * 1024]; 

    private final static int THREADS = 1; 
    private final static int TASKS = 16; 
    private final static int ITERATIONS = 10000; 

    public static void main(String[] args) { 

     // Initialize the array 
     for (int i = 0; i < intArray.length; i++) { 
      intArray[i] = i; 
     } 

     ForkJoinPool pool = new ForkJoinPool(THREADS); 

     // Run through ITERATIONS loops, subdividing the iteration into TASKS F-J subtasks 
     for (int i = 0; i < ITERATIONS; i++) { 
      pool.invoke(new RecursiveIterate(0, intArray.length)); 
     } 

     pool.shutdown(); 
    } 

    private static class RecursiveIterate extends RecursiveAction { 

     final int start; 
     final int end; 

     public RecursiveIterate(final int start, final int end) { 
      this.start = start; 
      this.end = end; 
     } 

     @Override 
     protected void compute() { 

      if ((end - start) <= (intArray.length/TASKS)) { 
       // We've reached the subdivision limit - iterate over the arrays 
       for (int i = start; i < end; i += 3) { 
        floatArray[i] += i + intArray[i]; 
       } 

      } else { 
       // Subdivide and start new tasks 
       final int mid = (start + end) >>> 1; 
       invokeAll(new RecursiveIterate(start, mid), new RecursiveIterate(mid, end)); 
      } 
     } 
    } 
} 
+0

看起來它是按照設計工作。您正在請求1的並行性,但是您在invokeAll中添加了兩個任務。 但我不是這方面的專家,所以我可能是錯的。 – 2011-03-31 00:56:51

+0

我以前從其他人那裏聽說過,將線程數設置爲「多一個」會修復一些問題。 – 2011-03-31 01:11:11

+0

回覆:Jochen - 據我瞭解框架,我們應該能夠添加任意數量的任務,而不管並行性級別(線程數量)。例如,我們可以遞歸地將一個大任務細分成256個獨立的小任務,但是我們應該能夠在少於256個處理器的機器上執行該算法。另外,死鎖不是直接的(正如我們所期望的那樣,如果2個任務/ 1個線程是非法的 - 相反它是在迭代次數不可預知的情況下發生的,但是我對FJ也是比較新穎的,所以我可能會誤解。 – AaronD 2011-03-31 03:40:34

回答

3

看起來像在ForkJoinPool中的錯誤。所有我能在課堂上看到的東西都適合你的例子。唯一的另一種可能性可能是你的任務之一拋出異常並且異常死亡(儘管這仍然應該被處理)。

+1

這是在實際上是ForkJoinPool中的一個錯誤。與@axtavt相反,它可以在JDK 1.7以及JDK 1.6 + jsr166y中重現。 我在一個單獨的論壇中與Doug Lea討論了這個問題,他斷定ForkJoinPool過早地終止了工作線程。該修復程序現已在http://gee.cs.oswego.edu/dl/concurrency-interest/index.html中檢入,並且可以在OpenJDK 1.7版本中很快獲得。 – AaronD 2011-04-01 22:42:04

+0

非常漂亮! – jtahlborn 2011-04-01 22:56:33