2011-03-30 20 views
7

Sto utilizzando jsr166y ForkJoinPool per distribuire attività di calcolo tra i thread. Ma chiaramente devo fare qualcosa di sbagliato.ForkJoinPool parallelism = 1 deadlock

Le mie attività sembrano funzionare in modo impeccabile se creo ForkJoinPool con parallelismo> 1 (il valore predefinito è Runtime.availableProcessors(); ho eseguito con 2-8 thread). Ma se creo ForkJoinPool con parallelism = 1, vedo deadlock dopo un numero imprevedibile di iterazioni.

Sì - il parallelismo di impostazione = 1 è una pratica strana. In questo caso sto analizzando un algoritmo parallelo con l'aumentare del numero di thread e voglio confrontare la versione parallela, eseguita con un singolo thread, con un'implementazione seriale di base, in modo da accertare con precisione il sovraccarico dell'implementazione parallela .

Di seguito è riportato un semplice esempio che illustra il problema che sto vedendo. Il 'compito' è una falsa iterazione su un array fisso, suddivisa in modo ricorsivo in 16 sottoattività.

Se eseguito con THREADS = 2 (o più), viene eseguito in modo affidabile fino al completamento, ma se eseguito con THREADS = 1, si verifica invariabilmente deadlock. Dopo un numero imprevedibile di iterazioni, il ciclo principale si blocca in ForkJoinPool.invoke(), in attesa su task.join() e il thread di lavoro termina.

Io corro con JDK 1.6.0_21 e 1.6.0_22 sotto Linux, e utilizzando una versione di jsr166y scaricato un paio di giorni fa dal sito di Doug Lea (http://gee.cs.oswego.edu/dl/concurrency-interest/index.html)

Eventuali suggerimenti per quello che mi manca ? Molte grazie in anticipo.

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

Sembra che funzioni come progettato. Si richiede il parallelismo di 1, ma in questo caso si aggiungono due attività in invokeAll. Ma non sono un esperto in questo, quindi potrei sbagliarmi. –

+0

L'ho già sentito da altre persone, che l'impostazione del numero di thread su "one more" risolve le cose. –

+0

Re: Jochen - Come ho capito il framework, dovremmo essere in grado di aggiungere un numero arbitrario di task, indipendentemente dal livello di parallelismo (thread count). Ad esempio, potremmo suddividere ricorsivamente un'attività di grandi dimensioni in 256 piccoli task separati, ma dovremmo essere in grado di eseguire quell'algoritmo su una macchina con meno di 256 processori. Inoltre, il deadlock non è immediato (come ci si aspetterebbe se 2 thread di operazioni/1 fosse illegale - invece è dopo un numero imprevedibile di iterazioni, ma sono relativamente nuovo anche a FJ, quindi potrei essere frainteso – AaronD

risposta

3

sembra un errore nel ForkJoinPool. tutto ciò che posso vedere nell'uso della classe si adatta al tuo esempio. l'unica altra possibilità potrebbe essere una delle tue attività che genera un'eccezione e muore in modo anomalo (anche se dovrebbe comunque essere gestito).

+1

Questo era in Infatti un bug in ForkJoinPool. Contrariamente a @axtavt, è riproducibile in JDK 1.7, così come JDK 1.6 + jsr166y. Ho discusso questo con Doug Lea in un forum separato, e ha concluso che ForkJoinPool stava terminando prematuramente il thread di lavoro. La correzione è ora archiviata e disponibile su http://gee.cs.oswego.edu/dl/concurrency-interest/index.html e dovrebbe essere disponibile a breve nelle build di OpenJDK 1.7. – AaronD

+0

cattura molto bella! – jtahlborn