深入探究PHP的多进程编程方法

子进程的创建
一般的子进程的写法是:

<?php $pid = pcntl_fork(); if($pid == -1){ //创建失败 die('could not fork'); } else{ if($pid){ //从这里开始写的代码是父进程的 exit("parent!"); } else{ //子进程代码,为防止不停的启用子进程造成系统资源被耗尽的情况,一般子进程代码运行完成后,加入exit来确保子进程正常退出。 exit("child"); } } ?>

上边的代码如果创建子进程成功的话,系统就有了2个进程,一个为父进程,一个为子进程,子进程的id号为$pid。在系统运行到$pid = pcntl_fork();时,在这个地方进行分支,父子进程各自开始运行各自的程序代码。代码的运行结果是parent 和child,很奇怪吧,为什么一个if和else互斥的代码中,都输出了结果?其实是像上边所说的,代码在pcntl_fork时,一个父进程运行parent,一个子进程运行了child。在代码结果上就显示了parent和child。至于谁先谁后的问题,这得要看系统资源的分配了。

如果需要起多个进程来处理数据,可以根据数据的数量,按照约定好的数量比如说1000条一个进程来起子进程。使用for循环就可以了。   

#如果获得的总数小于或等于0,等待60秒,并退出 if ($count <= 0) { sleep(60); exit; } #如果大于1000,计算需要起的进程数 if ($count > 1000) { $cycleSize = ceil($count/1000); } else { $cycleSize = 1; } for ($i=0; $i<$cycleSize; $i++) { $pid = pcntl_fork(); if($pid == -1) { break; } else { if($pid) { #父进程获得子进程的pid,存入数组 $pidArr[] = $pid; } else { //开始发送,子进程执行完自己的任务后,退出。 exit; } } } while(count($pidArr) > 0) { $myId = pcntl_waitpid(-1, $status, WNOHANG); foreach($pidArr as $key => $pid) { if($myId == $pid) unset($pidArr[$key]); } }

然后使用crontab,来使此PHP程序每隔一段时间自动执行。

当然,示例代码比较简单,具体还需要考虑怎么防止多个子进程执行到同一条数据或者当前进程处理数据未完成时,crontab又开始执行PHP文件启用新的进程等等。


PHP多进程实现方式
下面来系统地整理一下PHP多进程的实现方式:

1. 直接方式

pcntl_fork() 创建一个进程,在父进程返回值是子进程的pid,在子进程返回值是0,-1表示创建进程失败。跟C非常相似。

测试脚本 test.php
 

<?php // example of multiple processes date_default_timezone_set( 'Asia/Chongqing'); echo "parent start, pid ", getmypid(), "\n" ; beep(); for ($i=0; $i<3; ++$i){ $pid = pcntl_fork(); if ($pid == -1){ die ("cannot fork" ); } else if ($pid > 0){ echo "parent continue \n"; for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ echo "child start, pid ", getmypid(), "\n" ; for ($j=0; $j<5; ++$j){ beep(); } exit ; } } // *** function beep(){ echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ; sleep(1); } ?>

用命令行运行

#php -f test.php

输出结果

parent start, pid 1793 1793 2013-01-14 15:04:17 parent continue 1793 2013-01-14 15:04:18 child start, pid 1794 1794 2013-01-14 15:04:18 1794 2013-01-14 15:04:19 1793 2013-01-14 15:04:19 1794 2013-01-14 15:04:20 parent continue 1793 2013-01-14 15:04:20 child start, pid 1795 1795 2013-01-14 15:04:20 17931794 2013-01-14 15:04:212013-01-14 15:04:21 1795 2013-01-14 15:04:21 1794 2013-01-14 15:04:22 1795 2013-01-14 15:04:22 parent continue 1793 2013-01-14 15:04:22 child start, pid 1796 1796 2013-01-14 15:04:22 1793 2013-01-14 15:04:23 1796 2013-01-14 15:04:23 1795 2013-01-14 15:04:23 1795 2013-01-14 15:04:24 1796 2013-01-14 15:04:24 1796 2013-01-14 15:04:25 1796 2013-01-14 15:04:26

从中看到,创建了3个子进程,和父进程一起并行运行。其中有一行格式跟其他有些不同,
17931794                2013-01-14 15:04:212013-01-14 15:04:21
因为两个进程同时进行写操作,造成了冲突。


2. 阻塞方式

用直接方式,父进程创建了子进程后,并没有等待子进程结束,而是继续运行。似乎这里看不到有什么问题。如果php脚本并不是运行完后自动结束,而是常驻内存的,就会造成子进程无法回收的问题。也就是僵尸进程。可以通过pcntl_wai()方法等待进程结束,然后回收已经结束的进程。
将测试脚本改成:
 

$pid = pcntl_fork(); if ($pid == -1){ ... } else if ($pid > 0){ echo "parent continue \n"; pcntl_wait($status); for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ ... }

用命令行运行

#php -f test.php

输出结果

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/ea1d96977fad604b85d1f2aaf03ca12b.html