¿Por qué ForkJoinPool :: Invoke () bloquear el hilo principal?

Radu Murzea:

Exención de responsabilidad: Es la primera vez que estoy usando marco de Java Tenedor-Ingreso, así que no estoy 100% seguro de que estoy usando correctamente. También Java no es mi lenguaje de programación principal, por lo que este también podría ser relevante.


Dada la siguiente SSCCE :

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveAction;

class ForkCalculator extends RecursiveAction
{
    private final Integer[] delayTasks;

    public ForkCalculator(Integer[] delayTasks)
    {
        this.delayTasks = delayTasks;
    }

    @Override
    protected void compute()
    {
        if (this.delayTasks.length == 1) {
            this.computeDirectly();
            return;
        }

        Integer halfway = this.delayTasks.length / 2;

        ForkJoinTask.invokeAll(
            new ForkCalculator(
                Arrays.copyOfRange(this.delayTasks, 0, halfway)
            ),
            new ForkCalculator(
                Arrays.copyOfRange(this.delayTasks, halfway, this.delayTasks.length)
            )
        );
    }

    private void computeDirectly()
    {
        Integer delayTask = this.delayTasks[0];

        try {
            Thread.sleep(delayTask);
        } catch (InterruptedException ex) {
            System.err.println(ex.getMessage());
            System.exit(2);
        }

        System.out.println("Finished computing task with delay " + delayTask);
    }
}

public final class ForkJoinBlocker
{
    public static void main(String[] args)
    {
        ForkCalculator calculator = new ForkCalculator(
            new Integer[]{1500, 1400, 1950, 2399, 4670, 880, 5540, 1975, 3010, 4180, 2290, 1940, 510}
        );

        ForkJoinPool pool = new ForkJoinPool(
            Runtime.getRuntime().availableProcessors()
        );

        pool.invoke(calculator);

        //make it a daemon thread
        Timer timer = new Timer(true);

        timer.scheduleAtFixedRate(
            new TimerTask() {
                @Override
                public void run()
                {
                    System.out.println(pool.toString());
                }
            },
            100,
            2000
        );
    }
}

Así se crea un ForkJoinPoola que presento algunas tareas que hacer algo de procesamiento. Los reemplazó con Thread.sleep()los fines de este ejemplo, que sea sencillo.

En mi programa real, esta es una muy larga lista de tareas, por lo que quiero imprimir periódicamente el estado actual en el estándar de salida. Trato de hacer eso en un hilo separado mediante un trabajo planificado TimerTask.

Sin embargo, me di cuenta de algo que no esperaba: en mi ejemplo, la salida es algo así como:

Finished computing task with delay 1500
Finished computing task with delay 2399
Finished computing task with delay 1400
Finished computing task with delay 4180
Finished computing task with delay 1950
Finished computing task with delay 5540
Finished computing task with delay 880
.......

Lo que significa el "status-tarea" nunca se ejecuta.

Sin embargo, si modifico mi código para mover el pool.invoke(calculator);al final, entonces funciona como se esperaba:

java.util.concurrent.ForkJoinPool@59bf63ba[Running, parallelism = 4, size = 4, active = 4, running = 0, steals = 0, tasks = 5, submissions = 0]
Finished computing task with delay 1500
java.util.concurrent.ForkJoinPool@59bf63ba[Running, parallelism = 4, size = 4, active = 4, running = 0, steals = 0, tasks = 5, submissions = 0]
Finished computing task with delay 2399
Finished computing task with delay 1400
java.util.concurrent.ForkJoinPool@59bf63ba[Running, parallelism = 4, size = 4, active = 4, running = 0, steals = 0, tasks = 4, submissions = 0]
Finished computing task with delay 4180
Finished computing task with delay 1950
......

La única conclusión que puedo sacar es que ForkJoinPool::invoke()bloquea el principal rosca (sólo se recurre después de todas las tareas en la piscina son terminado).

Yo esperaba que el código en el principal hilo para continuar a ser ejecutado, mientras que las tareas en el tenedor-join-piscina se manejan de forma asíncrona .

Mi pregunta es: ¿Por qué sucede esto porque he usado el marco incorrectamente? ¿Hay algo que tengo que correcta en mi código?

Me di cuenta de uno de ForkJoinPools constructores tiene un boolean asyncModeparámetro, pero, por lo que puedo decir de la aplicación, esto es sólo para decidir entre FIFO_QUEUEy LIFO_QUEUEejecución-modos (no exactamente seguro de lo que son):

public ForkJoinPool(
    int parallelism,
    ForkJoinWorkerThreadFactory factory,
    UncaughtExceptionHandler handler,
    boolean asyncMode
) {
    this(checkParallelism(parallelism),
         checkFactory(factory),
         handler,
         asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
         "ForkJoinPool-" + nextPoolId() + "-worker-");
    checkPermission();
}
manouti:

Básicamente invoke()esperará a que toda la tarea hasta el final antes de regresar, por lo que sí el hilo principal es el bloqueo. Después de eso, el Timerno tiene tiempo para ejecutar porque se ejecuta en un hilo de utilidad.

Simplemente puede utilizar execute()en lugar de invoke()que se ejecuta la tarea asíncrona. A continuación, puede join()en el ForkJoinTaskque esperar el resultado, durante el cual el Timerestaría funcionando:

ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
pool.execute(calculator);

    //make it a daemon thread
Timer timer = new Timer(true);

timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            System.out.println(pool.toString());
        }
    }, 100, 2000);

calculator.join(); // wait for computation

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=176052&siteId=1
Recomendado
Clasificación