深入探究PHP的多进程编程方法
《PHP实例:深入探究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.至于谁先谁后的问题,这得要看系统资源的分配了.PHP实战 ??? 如果需要起多个进程来处理数据,可以根据数据的数量,依照约定好的数量比如说1000条一个进程来起子进程.使用for循环就可以了.???PHP实战 #如果获得的总数小于或等于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程序每隔一段时间自动执行.PHP实战 ??? 当然,示例代码比拟简单,具体还需要考虑怎么防止多个子进程执行到同一条数据或者当前进程处理数据未完成时,crontab又开始执行PHP文件启用新的进程等等.PHP实战
1. 直接方式PHP实战 pcntl_fork() 创建一个进程,在父进程返回值是子进程的pid,在子进程返回值是0,-1表现创建进程失败.跟C非常相似.PHP实战 测试脚本 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,"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个子进程,和父进程一起并行运行.其中有一行格式跟其他有些分歧,
用直接方式,父进程创建了子进程后,并没有等待子进程结束,而是继续运行.似乎这里看不到有什么问题.如果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 输出成果 parent start,pid 1807 1807 2013-01-14 15:20:05 parent continue child start,pid 1808 1808 2013-01-14 15:20:06 1808 2013-01-14 15:20:07 1808 2013-01-14 15:20:08 1808 2013-01-14 15:20:09 1808 2013-01-14 15:20:10 1807 2013-01-14 15:20:11 1807 2013-01-14 15:20:12 parent continue child start,pid 1809 1809 2013-01-14 15:20:13 1809 2013-01-14 15:20:14 1809 2013-01-14 15:20:15 1809 2013-01-14 15:20:16 1809 2013-01-14 15:20:17 1807 2013-01-14 15:20:18 1807 2013-01-14 15:20:19 child start,pid 1810 1810 2013-01-14 15:20:20 parent continue 1810 2013-01-14 15:20:21 1810 2013-01-14 15:20:22 1810 2013-01-14 15:20:23 1810 2013-01-14 15:20:24 1807 2013-01-14 15:20:25 1807 2013-01-14 15:20:26 父进程在pcntl_wait()将本身阻塞,等待子进程运行完了才接着运行.PHP实战
阻塞方式失去了多进程的并行性.还有一种办法,既可以回收已经结束的子进程,又可以并行.这就是非阻塞的方式. <?php // example of multiple processes date_default_timezone_set( 'Asia/Chongqing'); declare (ticks = 1); pcntl_signal(SIGCHLD,"garbage" ); echo "parent start,"n" ; for ($j=0; $j<5; ++$j){ beep(); } exit (0); } } // parent while (1){ // do something else sleep(5); } // *** function garbage($signal){ echo "signel $signal receivedn" ; while (($pid = pcntl_waitpid(-1,WNOHANG))> 0){ echo "t child end pid $pid,status $statusn" ; } } function beep(){ echo getmypid(),"n" ; sleep(1); } ?> 用命令行运行 #php -f test.php & 输出成果 parent start,pid 2066 2066 2013-01-14 16:45:34 parent continue 2066 2013-01-14 16:45:35 child start,pid 2067 2067 2013-01-14 16:45:35 20662067 2013-01-14 16:45:362013-01-14 16:45:36 2067 2013-01-14 16:45:37 parent continue 2066 2013-01-14 16:45:37 child start,pid 2068 2068 2013-01-14 16:45:37 2067 2013-01-14 16:45:38 2068 2013-01-14 16:45:38 2066 2013-01-14 16:45:38 parent continue 2066 2013-01-14 16:45:40 child start,pid 2069 2069 2067 2013-01-14 16:45:40 2013-01-14 16:45:40 2068 2013-01-14 16:45:40 2066 2013-01-14 16:45:41 2069 2013-01-14 16:45:41 2068 2013-01-14 16:45:41 signel 17 received child end pid 2067,status 0 2069 2013-01-14 16:45:42 2068 2013-01-14 16:45:42 2069 2013-01-14 16:45:43 signel 17 received child end pid 2068,status 0 2069 2013-01-14 16:45:44 signel 17 received child end pid 2069,status 0 多个进程又并行运行了,并且运行大约10秒钟之后,用 ps -ef | grep php 查看正在运行的进程,只有一个进程
pcntl_waitpid(-1,WNOHANG) $status ?返回子进程的结束状态PHP实战
windows系统不支持pcntl函数,幸好有curl_multi_exec()这个工具,利用内部的多线程,拜访多个链接,每个链接可以作为一个任务.PHP实战 编写脚本 test1.php <?php date_default_timezone_set( 'Asia/Chongqing'); $tasks = array( 'http://localhost/feedbowl/t2.php?job=task1','http://localhost/feedbowl/t2.php?job=task2','http://localhost/feedbowl/t2.php?job=task3' ); $mh = curl_multi_init(); foreach ($tasks as $i => $task){ $ch[$i] = curl_init(); curl_setopt($ch[$i],CURLOPT_URL,$task); curl_setopt($ch[$i],CURLOPT_RETURNTRANSFER,1); curl_multi_add_handle($mh,$ch[$i]); } do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // completed,checkout result foreach ($tasks as $j => $task){ if (curl_error($ch[$j])){ echo "task ${j} [$task ] error ",curl_error($ch[$j]),"rn" ; } else { echo "task ${j} [$task ] get: rn",curl_multi_getcontent($ch[$j]),"rn" ; } } ?> 编写脚本 test2.php <?php date_default_timezone_set( 'Asia/Chongqing'); echo "child start,"rn" ; for ($i=0; $i<5; ++$i){ beep(); } exit (0); // *** function beep(){ echo getmypid(),date('Y-m-d H:i:s',"rn"; sleep(1); } ?> 用命令行运行 #php -f test1.php & 输出成果 task 0 [http://localhost/feedbowl/t2.php?job=task1] get: child start,pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 task 1 [http://localhost/feedbowl/t2.php?job=task2] get: child start,pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 task 2 [http://localhost/feedbowl/t2.php?job=task3] get: child start,pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 从打印的光阴看到,多个任务几乎是同时运行的. 编程之家培训学院每天发布《PHP实例:深入探究PHP的多进程编程方法》等实战技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培养人才。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |