Источник.

Вся книга в pdf

Пример

PHP не поддерживает одновременный запуск кода, если вы не установите расширения, такие как pthread. Иногда это можно обойти, используя proc_open() и stream_set_blocking() и читая их вывод асинхронно.

Если мы разделим код на более мелкие куски, мы сможем запустить его как несколько процессов. Затем с помощью функции stream_set_blocking() мы можем сделать каждый подпроцесс также неблокирующим. Это означает, что мы можем порождать несколько подпроцессов, а затем проверять их вывод в цикле (аналогично четному циклу) и ждать, пока все они не завершатся.

В качестве примера у нас может быть небольшой подпроцесс, который просто запускает цикл и в каждой итерации спит случайным образом в течение 100-1000 мс (обратите внимание, задержка всегда одинакова для одного подпроцесса).

<?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");
}

 

Затем основной процесс порождает подпроцессы и читает их вывод. Мы можем разделить его на более мелкие блоки:

  • Порождем подпроцессы с proc_open() .
  • Делаем каждый подпроцесс неблокирующим с помощью stream_set_blocking().
  • Запускаем цикл, пока все подпроцессы не закончат использование proc_get_status().
  • Правильно закрываем дескрипторы файлов с выходным каналом для каждого подпроцесса с помощью fclose() и закрываем дескрипторы процессов с помощью proc_close().
<?php
// non-blocking-proc_open.php
// Файловые дескрипторы для каждого подпроцесса.
$descriptors = [
    0 => ['pipe', 'r'], // stdin
    1 => ['pipe', 'w'], // stdout
];

$pipes = [];
$processes = [];
foreach (range(1, 3) as $i) {
    // Создать подпроцесс.
    $proc = proc_open('php subprocess.php proc' . $i, $descriptors, $procPipes);
    $processes[$i] = $proc;
    // Сделать подпроцесс неблокирующим (только выходной канал).
    stream_set_blocking($procPipes[1], 0);
    $pipes[$i] = $procPipes;
}

// Запускать в цикле, пока не завершатся все подпроцессы.
while (array_filter($processes, function($proc) { return proc_get_status($proc)['running']; })) {
    foreach (range(1, 3) as $i) {
        usleep(10 * 1000); // 100ms
        // Прочитать весь доступный вывод (непрочитанный вывод буферизован).
        $str = fread($pipes[$i][1], 1024);
        if ($str) {
            printf($str);
        }
    }
}

// Закрыть все трубы и процессы.
foreach (range(1, 3) as $i) {
    fclose($pipes[$i][1]);
    proc_close($processes[$i]);
}

Выходные данные затем содержат смесь из всех трех подпроцессов, поскольку они считываются функцией fread() (обратите внимание, что в этом случае proc1 завершился намного раньше, чем два других):

$ 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