PHP 多进程

来源:互联网 发布:域名劫持软件 编辑:程序博客网 时间:2024/06/17 19:15
</pre><span style="padding:0px; margin:0px; color:rgb(51,51,51); font-family:微软雅黑,Verdana,sans-serif,宋体; font-size:13px; line-height:22.5px"></span><pre name="code" class="php"><span style="color: rgb(51, 51, 51); font-family: 微软雅黑, Verdana, sans-serif, 宋体; font-size: 13px; line-height: 22.5px; background-color: rgb(255, 255, 255);">1直接方式</span>
<span style="color: rgb(51, 51, 51); font-family: 微软雅黑, Verdana, sans-serif, 宋体; font-size: 13px; line-height: 22.5px; background-color: rgb(255, 255, 255);">pcntl_fork() 创建一个进程,在父进程返回值是子进程的pid,在子进程返回值是0,-1表示创建进程失败。跟C非常相似。 </span>

测试脚本 test.php 
?
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" );          }elseif($pid> 0){                 echo"parent continue \n";                 for($k=0;$k<2; ++$k){                      beep();                }          }elseif($pid== 0){                 echo"child start, pid ",getmypid(),"\n";                 for($j=0;$j<5; ++$j){                      beep();                }                 exit;          }    }    // ***    functionbeep(){           echogetmypid(),"\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){    ...}elseif($pid> 0){     echo"parent continue \n";     pcntl_wait($status);     for($k=0;$k<2; ++$k){          beep();    }}elseif($pid== 0){     ...}


?
$pid= pcntl_fork();if($pid== -1){    ...}elseif($pid> 0){     echo"parent continue \n";     pcntl_wait($status);     for($k=0;$k<2; ++$k){          beep();    }}elseif($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()将自己阻塞,等待子进程运行完了才接着运行。 


3. 非阻塞方式 

阻塞方式失去了多进程的并行性。还有一种方法,既可以回收已经结束的子进程,又可以并行。这就是非阻塞的方式。 
修改脚本: 
?
 
用命令行运行 
<?php    // example of multiple processes    date_default_timezone_set('Asia/Chongqing');    declare(ticks = 1);    pcntl_signal(SIGCHLD,"garbage");    echo"parent start, pid ",getmypid(),"\n";    beep();    for($i=0;$i<3; ++$i){          $pid= pcntl_fork();           if($pid== -1){                 die("cannot fork" );          }elseif($pid> 0){                 echo"parent continue \n";                 for($k=0;$k<2; ++$k){                      beep();                }          }elseif($pid== 0){                 echo"child start, pid ",getmypid(),"\n";                 for($j=0;$j<5; ++$j){                      beep();                }                 exit(0);          }    }    // parent    while(1){           // do something else          sleep(5);    }    // ***    functiongarbage($signal){           echo"signel $signal received\n" ;                      while(($pid= pcntl_waitpid(-1, $status, WNOHANG))> 0){                 echo"\t child end pid $pid , status $status\n" ;          }    }    functionbeep(){           echogetmypid(),"\t",date('Y-m-d H:i:s', time()), "\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 查看正在运行的进程,只有一个进程 
lqling    2066  1388  0 16:45 pts/1    00:00:00 php -f t5.php 
是父进程,子进程被回收了。 


4. 子进程退出状态 

pcntl_waitpid(-1, $status, WNOHANG) $status 返回子进程的结束状态 


5. windows多线程 

windows系统不支持pcntl函数,幸好有curl_multi_exec()这个工具,利用内部的多线程,访问多个链接,每个链接可以作为一个任务。 

编写脚本 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($tasksas$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($tasksas$j => $task){         if(curl_error($ch[$j])){              echo"task ${j} [$task ] error " , curl_error($ch[$j]),"\r\n";         }else{              echo"task ${j} [$task ] get: \r\n" , curl_multi_getcontent($ch[$j]),"\r\n";         }    }?>


?

编写脚本 test2.php 

<?php    date_default_timezone_set('Asia/Chongqing');    echo"child start, pid ",getmypid(),"\r\n";    for($i=0;$i<5; ++$i){         beep();    }    exit(0);    // ***    functionbeep(){        echogetmypid(),"\t",date('Y-m-d H:i:s' , time()), "\r\n";        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 

从打印的时间看到,多个任务几乎是同时运行的。
0 0