2011-03-30 17 views
7

私はスレッド間で計算タスクを配布するためにjsr166y ForkJoinPoolを使用しています。しかし、私は間違ったことをしているに違いない。ForkJoinPool並列処理= 1デッドロック

並列処理> 1(デフォルトはRuntime.availableProcessors();私は2〜8スレッドで実行しています)のForkJoinPoolを作成すると、私のタスクは完璧に機能しているようです。しかし、並列性= 1のForkJoinPoolを作成すると、予測できない繰り返し回数の後にデッドロックが発生します。

はい - parallelism = 1を設定するのは奇妙な方法です。この場合、スレッド数が増えるにつれて並列アルゴリズムをプロファイリングしています。並列実装のオーバーヘッドを正確に把握するために、並列バージョンを比較して、単一のスレッドで実行し、ベースラインのシリアル実装にしたい。

以下は、私が見ている問題を示す簡単な例です。 'タスク'は、固定配列上のダミー反復であり、再帰的に16個のサブタスクに分割されます。

THREADS = 2(またはそれ以上)で実行すると、確実に完了まで実行されますが、THREADS = 1で実行すると常にデッドロックになります。予期せぬ数の反復の後、メインループはForkJoinPool.invoke()でハングし、task.join()を待機し、ワーカースレッドは終了します。

私はLinuxでJDK 1.6.0_21および1.6.0_22で実行している、とjsr166yのバージョンを使用していダグ・リーのウ​​ェブサイトから数日前にダウンロード(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に2つのタスクを追加しています。 しかし、私はこれに関する専門家ではないので、私は間違っているかもしれません。 –

+0

これまで私はこれを他の人から聞いたことがあります。スレッドの数を「もう1つ」に設定すると物事が修正されます。 –

+0

Re:Jochen - 私はフレームワークを理解しているので、並列度(スレッド数)に関係なく、任意の数のタスクを追加することができます。たとえば、大きなタスクを再帰的に256個の小さなタスクに細分することができますが、256個未満のプロセッサを搭載したマシンでそのアルゴリズムを実行できるはずです。また、デッドロックは即時ではありません(2つのタスク/ 1つのスレッドが違法であると予想されるため、予想外の反復回数の後にありますが、FJにも比較的新しいので誤解される可能性があります)。 – AaronD

答えて

3

は、ForkJoinPoolのバグのようです。クラスの使用法でわかるすべてがあなたの例に合っています。他の唯一の可能性は、例外を投げて異常に死んでいるタスクの1つかもしれません。

+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

+0

非常に良いキャッチ! – jtahlborn