Чего-то не хватает в этом тестовом примере дочернего процесса php разветвления и ожидания? - PullRequest
1 голос
/ 08 июня 2011

Это всего лишь тестовый скрипт. Я тестирую базовую функциональность дочернего ветвления перед созданием простой библиотеки, которая позволяет мне порождать несколько процессов для параллельной обработки пакетов данных в php. Что-нибудь еще, что я должен проверить, чтобы понять, прежде чем продолжить? Я понимаю, что все ресурсы копируются во время разветвления, поэтому инициализируйте / откройте все ресурсы, необходимые после разветвления.

<?php

$childcount = 10;

for($i = 1; $i <= $childcount; $i ++)
{
    $pid = pcntl_fork();
    if ($pid == -1) {
         echo "failed to fork on loop $i of forking\n";
    } else if ($pid) {
         // we are the parent
$pidArray[$pid] = $pid;
         // and we want to wait on all children at the end of the loop
    } else {
         // we are the child
        echo "Child is outputting it's count and dying. Count: $i \n ";
        doMessage($i);
        die;
    }
}

echo "sleeping to see if child finished events queue\n";
sleep(10);

print_r($pidArray);

for($j = 1; $j <= $childcount; $j++)
{
    echo "parent is waiting on child\n";
    $pid = pcntl_wait($status); //Wait for random child to finish
    $pidArray[$pid] = "terminated";
    echo "parent found $j of the finished children\n";
}

print_r($pidArray);

function doMessage($location)
{
    sleep (rand(4,20));
    echo "outputting concurrently : $location \n"; 
}

Вывод выглядит так:

me@myhost:~/$]: php test.php
sleeping to see if child finished events queue
Child is outputting it's count and dying. Count: 3 
 Child is outputting it's count and dying. Count: 4 
 Child is outputting it's count and dying. Count: 5 
 Child is outputting it's count and dying. Count: 6 
 Child is outputting it's count and dying. Count: 8 
 Child is outputting it's count and dying. Count: 9 
 Child is outputting it's count and dying. Count: 10 
 Child is outputting it's count and dying. Count: 7 
 Child is outputting it's count and dying. Count: 2 
 Child is outputting it's count and dying. Count: 1 
 outputting concurrently : 9 
outputting concurrently : 1 
outputting concurrently : 6 
Array
(
    [22700] => 22700
    [22701] => 22701
    [22702] => 22702
    [22703] => 22703
    [22704] => 22704
    [22705] => 22705
    [22706] => 22706
    [22707] => 22707
    [22708] => 22708
    [22709] => 22709
)
parent is waiting on child
parent found 1 of the finished children
parent is waiting on child
parent found 2 of the finished children
parent is waiting on child
parent found 3 of the finished children
parent is waiting on child
outputting concurrently : 5 
parent found 4 of the finished children
parent is waiting on child
outputting concurrently : 2 
parent found 5 of the finished children
parent is waiting on child
outputting concurrently : 3 
parent found 6 of the finished children
parent is waiting on child
outputting concurrently : 8 
parent found 7 of the finished children
parent is waiting on child
outputting concurrently : 7 
parent found 8 of the finished children
parent is waiting on child
outputting concurrently : 4 
outputting concurrently : 10 
parent found 9 of the finished children
parent is waiting on child
parent found 10 of the finished children
Array
(
    [22700] => terminated
    [22701] => terminated
    [22702] => terminated
    [22703] => terminated
    [22704] => terminated
    [22705] => terminated
    [22706] => terminated
    [22707] => terminated
    [22708] => terminated
    [22709] => terminated
)

Я также подтвердил, что дополнительный pctl_wait сразу же вернется с pid -1.

1 Ответ

1 голос
/ 08 июня 2011

Ваш пример не затрагивает проблемы, которые вы можете решить с помощью pcntl_fork.

Помните, что fork () создает копию программы, что означает, что все дескрипторы скопированы. К сожалению, это довольно плохая ситуация для программы PHP, потому что большинство дескрипторов обрабатываются PHP или расширением PHP внутри.

Простой и, вероятно, "правильный" способ решения этой проблемы - это разветвление заранее, в действительности не должно быть необходимости разветвляться во многих различных точках программы, вы просто разветвляетесь, а затем делегируете работу. Используйте основную / рабочую иерархию.

Например, если вам нужно иметь много процессов, использующих MySQL Connection, просто выполните разветвление перед установлением соединения, чтобы каждый дочерний процесс имел свое собственное соединение с mysql, которым он управляет, и только он этим управляет.

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

Когда умирает ребенок, родитель посылает сигнал (SIGCHLD). Затем родитель может обработать смерть ребенка для внутренней обработки. Правильный способ разомкнуть ребенка - использовать pcntl_waitpid (). Вы можете использовать эту функцию, чтобы дождаться смерти ребенка или обнаружить, что ребенок уже умер. Используйте pcntl_wait (), когда вы хотите сделать это для множества детей. Посмотрите в соответствующем разделе руководства по PHP дополнительные параметры (включая указание функции не прерывать нормальную работу).

Однако использование SIGCHLD не всегда надежно. Когда вы быстро создаете много недолговечных потомков, обработка SIGCHLD в сочетании с pcntl_waitpid () может не обрабатывать все процессы зомби.

Надеюсь, это помогло, документация на php.net в порядке, но, на мой взгляд, она могла бы пойти глубже в предметах и ​​потенциальных ловушках, поскольку эти функции легко злоупотреблять.

Вот тривиальный пример (извлечен из комментария php.net) , который показывает неправильное распределение ресурсов для родительского потока:

<?php 
mysql_connect(/* enter a working server here maybe? */); 
$f=pcntl_fork();

while(true){    
    sleep(rand(0,10)/100); 
    $r=mysql_query("select $f;"); 

    if(!$r)die($f.": ".mysql_error()."\n"); 
      list($x)=mysql_fetch_array($r); 
      echo ($f)?".":"-"; 
    if($x!=$f) echo ($f.": fail: $x!=$f\n "); 
} 
?>

Выполнение этой команды на cli приведет к различным результатам:

  • очень часто просто зависает и больше ничего не выводит

  • также очень часто сервер закрывает соединение, возможно потому, что он получает чередующиеся запросы, которые он не может обработать.

  • иногда один процесс получает результат ДРУГИХ процессов запрос! (потому что оба посылают свои запросы по одному сокету, и это просто удача, кто получит ответ)

Надеюсь, это поможет, если вы расширите свой пример для извлечения данных и / или использования ресурсов.

С праздником!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...