php子进程分叉和等待的测试示例中缺少什么?

时间:2011-06-07 20:01:07

标签: php fork

这只是一个测试脚本。我正在测试基本的子fork功能,然后创建一个简单的lib,让我产生多个进程来在php中并行处理数据批处理。在继续之前还有其他我应该测试的东西吗?我知道所有资源都是在fork期间复制的,所以初始化/打开fork之后所需的所有资源。

<?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 个答案:

答案 0 :(得分:1)

您的示例不会触及您可以使用pcntl_fork加密的问题。

请记住,fork()会复制程序,这意味着会复制所有描述符。不幸的是,对于PHP程序来说这是一个相当糟糕的情况,因为大多数描述符都是由PHP或PHP扩展内部处理的。

解决这个问题的简单,可能是“正确”的方法是先分叉,确实不需要在程序中的许多不同点进行分叉,你只需要fork,然后委托工作。使用主/工作层次结构。

例如,如果你需要有许多使用MySQL连接的进程,只需在建立连接之前进行fork,那么每个子进程都有自己与mysql的连接,并且只有它自己管理。

另一件需要注意的事情是儿童过程何时死亡。死亡应由父母处理。如果不是,那么孩子就变成了僵尸:它不消耗资源,但它仍然是一个带有PID的过程。这是不可取的,因为大多数(全部?)操作系统对其可以处理的进程有一个上限。

当孩子死亡时,会向父母(SIGCHLD)发送信号。然后,父母可以处理孩子的死亡以进行内部处理。解密孩子的正确方法是使用pcntl_waitpid()。您可以使用该功能等待孩子死亡,或者检测到孩子已经死亡。如果要为无数个孩子执行此操作,请使用pcntl_wait()。查看PHP手册的相关部分以获取更多选项(包括让函数知道不要暂停正常操作)。

然而,使用SIGCHLD并不总是万无一失。当您快速创建许多短期子项时,与pcntl_waitpid()一起处理SIGCHLD可能无法处理所有僵尸进程。

希望这有帮助,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上运行此功能将会产生不同的结果:

  • 经常只是挂起而不再输出任何东西

  • 也常常会关闭连接,可能是因为它接收到无法处理的交错请求。

  • 有时一个进程会获得OTHER进程的结果 查询! (因为它们都在同一个套接字上发送查询, 得到答复的是纯粹的运气)

希望当您扩展示例以获取数据和/或使用资源时,这会有所帮助。

快乐的意识!