2011-03-30 2 views
7

스레드 사이에서 계산 작업을 분산하기 위해 jsr166y ForkJoinPool을 사용하고 있습니다. 그러나 나는 틀린 일을 분명히하고 있어야합니다.ForkJoinPool parallelism = 1 deadlock

병렬 처리가 1보다 큰 ForkJoinPool (기본값은 Runtime.availableProcessors()이고 2-8 스레드로 실행 중입니다)을 만들면 내 작업이 완벽하게 작동하는 것 같습니다. 그러나 parallelism = 1 인 ForkJoinPool을 만들면 예기치 않은 반복 횟수가 발생하면 교착 상태가 발생합니다.

예 - 병렬 처리 = 1로 설정하는 것은 이상한 방법입니다. 이 경우, 스레드 수가 증가함에 따라 병렬 알고리즘을 프로파일 링하고 있는데, 병렬 구현의 오버 헤드를 정확하게 확인하기 위해 병렬 직렬 버전을 비교하여 단일 스레드와 함께 실행하여 병렬 직렬 버전을 비교하려고합니다. .

다음은 내가 본 문제를 보여주는 간단한 예입니다. '작업'은 고정 배열의 더미 반복이며, 16 개의 하위 작업으로 반복적으로 나뉘어져 있습니다.

THREADS = 2 (이상)로 실행하면 THREADS = 2 이상으로 실행하면 안정적으로 실행되지만 THREADS = 1로 실행하면 항상 교착 상태가됩니다. 예측할 수없는 반복 횟수가 지나면 main 루프가 ForkJoinPool.invoke()에서 멈추고 task.join()을 기다리고 작업자 스레드가 종료됩니다.

나는 리눅스에서 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에 두 개의 작업을 추가하려고합니다. 그러나 나는 이것에 대한 전문가가 아니므로 잘못되었을 수도 있습니다. –

+0

다른 사람들로부터 전에이 말을 들었습니다. 스레드의 수를 '하나 더'로 설정하면 문제가 해결됩니다. –

+0

Re : Jochen - 프레임 워크를 이해하면서 병렬 처리 수준 (스레드 수)에 관계없이 임의의 수의 작업을 추가 할 수 있어야합니다. 예를 들어, 큰 작업을 반복적으로 256 개의 작은 작업으로 세분 할 수 있지만 256 개 미만의 프로세서가있는 시스템에서이 알고리즘을 실행할 수 있어야합니다. 또한, 교착 상태가 즉각적이지는 않습니다 (예를 들어, 2 개의 작업/1 개의 스레드가 불법 인 경우 예상 할 수있는 것처럼 예측할 수없는 반복 횟수가 반복됩니다.) 그러나 FJ도 비교적 새로운 개념이므로 오해의 소지가 있습니다. – AaronD

답변

3

은 ForkJoinPool의 버그처럼 보입니다. 내가 사용하는 수업에서 볼 수있는 모든 것이 당신의 모범이된다. 다른 가능성은 예외를 던지고 비정상적으로 죽는 작업 중 하나 일 수 있습니다 (그래도 처리해야하지만).

+1

This was in 사실 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