当前位置:首页 > PHP教程 > php高级应用 > 列表

php 的多进程操作实践案例分析

发布:smiling 来源: PHP粉丝网  添加日期:2022-02-15 10:38:17 浏览: 评论:0 

本文实例讲述了php 的多进程操作,分享给大家供大家参考,具体如下:

php的多进程处理依赖于pcntl扩展,通过pcntl_fork创建子进程来进行并行处理。

例1如下:

  1. <?php 
  2. $pid = pcntl_fork(); 
  3.  
  4. if($pid == -1) { 
  5.   //错误处理:创建子进程失败时返回-1. 
  6.   die('fork error'); 
  7. else if ($pid) { 
  8.   //父进程会得到子进程号,所以这里是父进程执行的逻辑 
  9.   echo "parent \n"
  10.   //等待子进程中断,防止子进程成为僵尸进程。 
  11.   pcntl_wait($status); 
  12. else { 
  13.   //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。 
  14.   echo "child \n"
  15.  
  16.   exit

pcntl_fork创建了子进程,父进程和子进程都继续向下执行,而不同是父进程会获取子进程的$pid也就是$pid不为零,而子进程会获取$pid为零,通过if else语句判断$pid我们就可以在指定位置写上不同的逻辑代码。

上述代码会分别输出parent和child。那么输出的parent和child是否会有顺序之分?是父进程会先执行?

例2如下:

  1. <?php 
  2. $pid = pcntl_fork(); 
  3.  
  4. if($pid == -1) { 
  5.   die('fork error'); 
  6. else if ($pid) { 
  7.   sleep(3); 
  8.   echo "parent \n"
  9.   pcntl_wait($status); 
  10. else { 
  11.   echo "child \n"
  12.  
  13.   exit

我们在父进程中通过sleep来延缓执行,看看效果。

结果是,很快输出了child,等待了接近3秒后,才输出parent,所以父进程和子进程的执行是相对独立的,没有先后之分。

那么问题又来了?pcntl_wait是做什么用的?

会挂起当前进程,直到子进程退出,如果子进程在调用此函数之前就已退出,此函数会立刻返回,子进程使用的资源将被释放。

例3如下:

  1. <?php 
  2. $pid = pcntl_fork(); 
  3.  
  4. if($pid == -1) { 
  5.   die('fork error'); 
  6. else if ($pid) { 
  7.   pcntl_wait ($status); 
  8.   echo "parent \n"
  9. else { 
  10.   sleep(3); 
  11.   echo "child \n"
  12.  
  13.   exit

上述代码,我们可以看到,父进程执行pcntl_wait时就已经挂起,直到等待3秒后输出child,子进程退出后,父进程继续执行,输出parent。

例4如下:

  1. <?php 
  2. define('FORK_NUMS', 3); 
  3.  
  4. $pids = array(); 
  5.  
  6. for($i = 0; $i < FORK_NUMS; ++$i) { 
  7.   $pids[$i] = pcntl_fork(); 
  8.   if($pids[$i] == -1) { 
  9.     die('fork error'); 
  10.   } else if ($pids[$i]) { 
  11.     pcntl_waitpid($pids[$i], $status); 
  12.     echo "pernet \n"
  13.   } else { 
  14.     sleep(3); 
  15.     echo "child id:" . getmypid() . " \n"
  16.     exit
  17.   } 

上述代码,我们创建3个子进程,父进程分别挂起等待子进程结束后,输出parent。

输出结果如下:

  1. child id:19090 
  2. pernet 
  3. child id:19091 
  4. pernet 
  5. child id:19092 
  6. pernet 

例5如下:

  1. <?php 
  2. define('FORK_NUMS', 3); 
  3.  
  4. $pids = array(); 
  5.  
  6. for($i = 0; $i < FORK_NUMS; ++$i) { 
  7.   $pids[$i] = pcntl_fork(); 
  8.   if($pids[$i] == -1) { 
  9.     die('fork error'); 
  10.   } else if ($pids[$i]) { 
  11.  
  12.   } else { 
  13.     sleep(3); 
  14.     echo "child id:" . getmypid() . " \n"
  15.     exit
  16.   } 
  17.  
  18. foreach($pids as $k => $v) { 
  19.   if($v) { 
  20.     pcntl_waitpid($v$status); 
  21.     echo "parent \n"
  22.   } 

输出结果如下:

  1. child id:19118 
  2. child id:19119 
  3. child id:19120 
  4. parent 
  5. parent 
  6. parent 

为什么上述代码跟例4的输出结果不一样?

我们可以看到例5的pcntl_waitpid函数放在了foreach中,foreach代码是在主进程中,也就是父进程的代码中。当执行foreach时,可能子进程已经全部执行完毕并退出。pcntl_waitpid会立刻返回,连续输出三个parent。

(*在子进程中,需通过exit来退出,不然会产生递归多进程,父进程中不需要exit,不然会中断多进程。)

例6如下:

  1. <?php 
  2. define('FORK_NUMS', 3); 
  3.  
  4. $pids = array(); 
  5.  
  6. $fp = fopen('./test.log''wb'); 
  7. $num = 1; 
  8.  
  9. for($i = 0; $i < FORK_NUMS; ++$i) { 
  10.   $pids[$i] = pcntl_fork(); 
  11.   if($pids[$i] == -1) { 
  12.     die('fork error'); 
  13.   } else if ($pids[$i]) { 
  14.  
  15.  
  16.   } else { 
  17.     for($i = 0; $i < 5; ++$i) { 
  18.  
  19.       flock($fp, LOCK_EX); 
  20.       fwrite($fpgetmypid() . ' : ' . date('Y-m-d H:i:s') . " : {$num} \r\n"); 
  21.  
  22.       flock($fp, LOCK_UN); 
  23.       echo getmypid(), ": success \r\n"
  24.       ++$num
  25.     } 
  26.     exit
  27.   } 
  28.  
  29. foreach($pids as $k => $v) { 
  30.   if($v) { 
  31.     pcntl_waitpid($v$status); 
  32.   } 
  33.  
  34. fclose($fp); 

代码如上:我们创建三个子进程,来同时向test.log文件写入内容,test.log内容如下:

  1. 19507 : 2016-03-16 20:40:52 : 1 
  2. 19507 : 2016-03-16 20:40:52 : 2 
  3. 19507 : 2016-03-16 20:40:52 : 3 
  4. 19507 : 2016-03-16 20:40:52 : 4 
  5. 19507 : 2016-03-16 20:40:52 : 5 
  6. 19509 : 2016-03-16 20:40:52 : 1 
  7. 19509 : 2016-03-16 20:40:52 : 2 
  8. 19509 : 2016-03-16 20:40:52 : 3 
  9. 19509 : 2016-03-16 20:40:52 : 4 
  10. 19509 : 2016-03-16 20:40:52 : 5 
  11. 19508 : 2016-03-16 20:40:52 : 1 
  12. 19508 : 2016-03-16 20:40:52 : 2 
  13. 19508 : 2016-03-16 20:40:52 : 3 
  14. 19508 : 2016-03-16 20:40:52 : 4 
  15. 19508 : 2016-03-16 20:40:52 : 5 

我们可以看到三个子进程的pid,它们分别执行了5次,时间几乎是在同时。但是$num的值并没像我们期望的那样从1-15进行递增。子进程中的变量是各自独立的,互不影响。子进程会自动复制父进程空间里的变量。

如何在进程中共享数据?

我们通过php的共享内存函数shmop来实现。

  1. <?php 
  2. define('FORK_NUMS', 3); 
  3.  
  4. $pids = array(); 
  5.  
  6. $fp = fopen('./test.log''wb'); 
  7. $num = 1; 
  8. //共享内存段的key 
  9. $shmKey = 123; 
  10. //创建共享内存段 
  11. $shmId = shmop_open($shmKey'c', 0777, 64); 
  12. //写入数据到共享内存段 
  13. shmop_write($shmId$num, 0); 
  14.  
  15. for($i = 0; $i < FORK_NUMS; ++$i) { 
  16.   $pids[$i] = pcntl_fork(); 
  17.   if($pids[$i] == -1) { 
  18.     die('fork error'); 
  19.   } else if ($pids[$i]) { 
  20.  
  21.     //阻塞,等待子进程退出 
  22.  
  23.     //注意这里,如果是非阻塞的话,$num的计数会出现问题。 
  24.     pcntl_waitpid($pids[$i], $status); 
  25.   } else { 
  26.     //读取共享内存段中的数据 
  27.     $num = shmop_read($shmId, 0, 64); 
  28.     for($i = 0; $i < 5; ++$i) { 
  29.       fwrite($fpgetmypid() . ' : ' . date('Y-m-d H:i:s') . " : {$num} \r\n"); 
  30.       echo getmypid(), ": success \r\n"
  31.       //递增$num 
  32.       $num = intval($num) + 1; 
  33.     } 
  34.  
  35.     //写入到共享内存段中 
  36.  
  37.     shmop_write($shmId$num, 0); 
  38.     exit
  39.   } 
  40.  
  41. //shmop_delete不会实际删除该内存段,它将该内存段标记为删除。 
  42. shmop_delete($shmId); 
  43. shmop_close($shmId); 
  44. fclose($fp); 

上述代码的运行结果如下:

  1. 19923 : 2016-03-17 00:05:18 : 1 
  2. 19923 : 2016-03-17 00:05:18 : 2 
  3. 19923 : 2016-03-17 00:05:18 : 3 
  4. 19923 : 2016-03-17 00:05:18 : 4 
  5. 19923 : 2016-03-17 00:05:18 : 5 
  6. 19924 : 2016-03-17 00:05:18 : 6 
  7. 19924 : 2016-03-17 00:05:18 : 7 
  8. 19924 : 2016-03-17 00:05:18 : 8 
  9. 19924 : 2016-03-17 00:05:18 : 9 
  10. 19924 : 2016-03-17 00:05:18 : 10 
  11. 19925 : 2016-03-17 00:05:18 : 11 
  12. 19925 : 2016-03-17 00:05:18 : 12 
  13. 19925 : 2016-03-17 00:05:18 : 13 
  14. 19925 : 2016-03-17 00:05:18 : 14 
  15. 19925 : 2016-03-17 00:05:18 : 15 

这样我们就在进程间共享了$num的数据。

Tags: php多进程

分享到: