PHP Création de processus non bloquants avec proc_open ()


Exemple

PHP ne prend pas en charge l'exécution du code simultanément, sauf si vous installez des extensions telles que pthread . Cela peut parfois être contourné en utilisant proc_open() et stream_set_blocking() et en lisant leur sortie de manière asynchrone.

Si nous divisons le code en morceaux plus petits, nous pouvons l'exécuter sous la forme de plusieurs suprocesses. Ensuite, en utilisant la fonction stream_set_blocking() , nous pouvons également rendre chaque sous-processus non bloquant. Cela signifie que nous pouvons générer plusieurs sous-processus, puis vérifier leur sortie dans une boucle (similaire à une boucle paire) et attendre qu'ils soient tous terminés.

Par exemple, nous pouvons avoir un petit sous-processus qui exécute une boucle et qui, dans chaque itération, dort aléatoirement pendant 100 à 1000 ms (notez que le délai est toujours le même pour un sous-processus).

<?php
// subprocess.php
$name = $argv[1];
$delay = rand(1, 10) * 100;
printf("$name delay: ${delay}ms\n");

for ($i = 0; $i < 5; $i++) {
    usleep($delay * 1000);
    printf("$name: $i\n");
}

Ensuite, le processus principal va générer des sous-processus et lire leur sortie. Nous pouvons le diviser en blocs plus petits:

<?php
// non-blocking-proc_open.php
// File descriptors for each subprocess.
$descriptors = [
    0 => ['pipe', 'r'], // stdin
    1 => ['pipe', 'w'], // stdout
];

$pipes = [];
$processes = [];
foreach (range(1, 3) as $i) {
    // Spawn a subprocess.
    $proc = proc_open('php subprocess.php proc' . $i, $descriptors, $procPipes);
    $processes[$i] = $proc;
    // Make the subprocess non-blocking (only output pipe).
    stream_set_blocking($procPipes[1], 0);
    $pipes[$i] = $procPipes;
}

// Run in a loop until all subprocesses finish.
while (array_filter($processes, function($proc) { return proc_get_status($proc)['running']; })) {
    foreach (range(1, 3) as $i) {
        usleep(10 * 1000); // 100ms
        // Read all available output (unread output is buffered).
        $str = fread($pipes[$i][1], 1024);
        if ($str) {
            printf($str);
        }
    }
}

// Close all pipes and processes.
foreach (range(1, 3) as $i) {
    fclose($pipes[$i][1]);
    proc_close($processes[$i]);
}

La sortie contient alors un mélange des trois sous-processus tels qu'ils sont lus par fread () (notez que dans ce cas, proc1 s'est terminé beaucoup plus tôt que les deux autres):

$ php non-blocking-proc_open.php 
proc1 delay: 200ms
proc2 delay: 1000ms
proc3 delay: 800ms
proc1: 0
proc1: 1
proc1: 2
proc1: 3
proc3: 0
proc1: 4
proc2: 0
proc3: 1
proc2: 1
proc3: 2
proc2: 2
proc3: 3
proc2: 3
proc3: 4
proc2: 4