当前位置:  编程技术>php
本页文章导读:
    ▪PHP 多进程的实现代码一例      代码:   代码示例: <?php //while(1)//循环采用3个进程 //{ //declare(ticks=1); $bWaitFlag = FALSE; // 是否等待进程结束 //$bWaitFlag = TRUE; // 是否等待进程结束 $intNum = 3; // 进程总数 $pids = array(); // .........
    ▪php守护进程函数 后台执行脚本的实例详解      我们经常通过crontab定时执行后端脚本。比如每10秒检查一下用户状态。 例子:   代码示例: @file: /php_scripts/scan_userstatus.php #!/usr/bin/env php -q  <?php  $status = has_goaway();  if ($status) {      //d.........
    ▪PHP多进程并发控制的测试实例      于是,准备用该PHP命令行程序生成多个子进程,将串行处理变成并行处理。 最简单的方法,在PHP中用exec()或popen()函数将一个shell命令行推到后台去执行,例如:   代码示例: <?php exec("/bin.........

[1]PHP 多进程的实现代码一例
    来源: 互联网  发布时间: 2013-12-24

代码:
 

代码示例:
<?php
//while(1)//循环采用3个进程
//{
//declare(ticks=1);
$bWaitFlag = FALSE; // 是否等待进程结束
//$bWaitFlag = TRUE; // 是否等待进程结束
$intNum = 3; // 进程总数
$pids = array(); // 进程PID数组
for($i = 0; $i <$intNum; $i++){
        // 产生子进程,而且从当前行之下开试运行代码,而且不继承父进程的数据信息
        $pids[$i] = pcntl_fork();
        /*if($pids[$i])//父进程
          {
        //echo $pids[$i]."parent"."$i -> " . time(). "\n";
        }
         */
        if($pids[$i] == -1){
           echo "couldn't fork". "\n";
        }elseif(!$pids[$i]){
          sleep(1);
          echo "\n"."第".$i."个进程 -> " . time(). "\n";
          //$url=" http://xxx/comments.php?p=".$i;//抓取页面的例子
          //$content = file_get_contents($url);
         //file_put_contents('message.txt',$content);
         //echo "\n"."第".$i."个进程 -> " ."抓取页面".$i."-> " . time()."\n";
         exit(0);//子进程要exit否则会进行递归多进程,父进程不要exit否则终止多进程
        }  
        if ($bWaitFlag){
                pcntl_waitpid($pids[$i], $status, WUNTRACED);
                echo "wait $i -> " . time() . "\n";
        }
}
//sleep(1);
} //by www.
?>

fork:操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像是兄弟关系,这两个进程共享代码空间,但是数据空间是相互独立的,子进程数据空间的内容是父进程的完整拷贝,指令指针也完全相同。
但只用一点不同,如果fork成功, 子进程fork的返回值是0 父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错,

2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因。

至于那一个最先运行,可能与操作系统有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决。

fork前父进程的东西子进程可以继承,而在fork后子进程没有任何和父进程的继承关系了。在子进程里创建的东西是子进程的,在父进程创建的东西是父进程的。可以完全看成两个进程。

在程序段里用了fork();之后程序出了分岔,派生出了两个进程。具体哪个先运行就看该系统的调度算法了。

可以这么认为,在运行到”pid=fork();”时系统派生出一个跟主程序一模一样的子进程。该进程的”pid=fork();”一句中 pid得到的就是子进程本身的pid;子进程结束后,父进程的”pid=fork();”中pid得到的就是父进程本身的pid。因此改程序有两行输出。

fork()函数复制了当前进程的PCB,并向父进程返回了派生子进程的pid。而且根据上面”corand”兄的提示,父子进程并行,打印语句的 先后完全看系统的调度算法。打印的内容控制则靠pid变量来控制。因为我们知道fork()向父进程返回了派生子进程的pid,是个正整数;而派生子进程 的pid变量并没有被改变。这一区别使得我们看到了他们的不同输出。

1,派生子进程的进程,即父进程,其pid不变;

2,对子进程来说,fork返回给它0,但它的pid绝对不会是0;之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;

3,fork之后夫子进程除非采用了同步手段,否则不能确定谁先运行,也不能确定谁先结束。
认为子进程结束后父进程才从fork返回的,这是不对的,fork不是这样的,vfork才这样。


    
[2]php守护进程函数 后台执行脚本的实例详解
    来源: 互联网  发布时间: 2013-12-24

我们经常通过crontab定时执行后端脚本。比如每10秒检查一下用户状态。
例子:
 

代码示例:
@file: /php_scripts/scan_userstatus.php
#!/usr/bin/env php -q  <?php  $status = has_goaway();  if ($status) {      //done  }  ?>

通过crontab定时执行脚本scan_userstatus.php
 

代码示例:
#echo “*:*/10 * * * * /php_scripts/scan_userstatus.php”

这样,每隔10秒钟,就会执行该脚本。

在短时间内,该脚本的内存资源还没有释放完,又启用了新的脚本。
即,新脚本启动了,旧脚本占用的资源还没有如愿释放。
如此,日积月累,浪费了很多内存资源。

对这个脚本进行如下的改进:
 

代码示例:
@file: /php_scripts/scan_userstatus.php
#/usr/bin/env php -q  <?php  while (1) {      $status = has_goaway();      if ($status) {          //done      }      usleep(10000000);  }  ?>

这样,不需要crontab了。可以通过以下命令执行脚本,达到相同的功能效果
 

代码示例:
#chmod +x /php_scripts/scan_userstatus.php
#nohup /php_scripts/scan_userstatus.php &
 

在这里,通过&将脚本放到后台运行,为了防止随着终端会话窗口关闭进程被杀,使用了nohup命令。
有办法不使nohup命令,也能够运行吗,就像Unin/Linux Daemon一样。

这里就要讲到守护进程函数了。

什么是守护进程?一个守护进程通常补认为是一个不对终端进行控制的后台任务。
它有三个很显著的特征:在后台运行,与启动他的进程脱离,无须控制终端。
常用的实现方式是fork() -> setsid() -> fork() 详细如下:
 

代码示例:

@file: /php_scripts/scan_userstatus.php

#/usr/bin/env php -q  <?php  daemonize();  while (1) {      $status = has_goaway();      if ($status) {          //done      }      usleep(10000000);  }     function daemonize() {      $pid = pcntl_fork();      if ($pid === -1 ) {          return FALSE;      } else if ($pid) {          usleep(500);          exit();                //exit parent      }         chdir("/");      umask(0);      $sid = posix_setsid();      if (!$sid) {          return FALSE;      }         $pid = pcntl_fork();      if ($pid === -1) {          return FALSE;      } else if ($pid) {          usleep(500);          exit(0);      }         if (defined('STDIN')) {          fclose(STDIN);      }      if (defined('STDOUT')){          fclose(STDOUT);      }      if (defined('STDERR')) {          fclose(STDERR);      }  }  ?>

实现了守护进程函数以后,则可以建立一个常驻进程,所以只需要执行一次:
 

代码示例:
#/php_scripts/scan_userstatus.php

关键的二个php函数是pcntl_fork()和posix_setsid()。

fork()一个进程,则表示创建了一个运行进程的副本,副本被认为是子进程,而原始进程被认为是父进程。当fork()运行之后,则可以脱离启动他的进程与终端控制等,也意味着父进程可以自由退出。

pcntl_fork()返回值,-1表示执行失败,0表示在子进程中,而返进程ID号,则表示在父进程中。在这里,退出父进程。setsid(),它首先使新进程成为一个新会话的“领导者”,最后使该进程不再控制终端,这也是成为守护进程最关键的一步,这意味着,不会随着终端关闭而强制退出进程。对于一个不会被中断的常驻进程来说,这是很关键的一步。进行最后一次fork(),这一步不是必须的,但通常都这么做,它最大的意义是防止获得控制终端。(在直接打开一个终端设备,而且没有使用O_NOCTTY标志的情况下, 会获得控制终端).

总结:
1) chdir() 将守护进程放到总是存在的目录中,另外一个好处是,你的常驻进程不会限制你umount一个文件系统。

2)umask() 设置文件模式,创建掩码到最大的允许限度。
如果一个守护进程需要创建具有可读,可写权限的文件,一个被继承的具有更严格权限的掩码会有反作用。

3)fclose(STDIN), fclose(STDOUT), fclose(STDERR) 关闭标准I/O流。注意,如果有输出(echo),则守护进程会失败。
通常将STDIN, STDOUT, STDERR重定向某个指定文件.


    
[3]PHP多进程并发控制的测试实例
    来源: 互联网  发布时间: 2013-12-24

于是,准备用该PHP命令行程序生成多个子进程,将串行处理变成并行处理。
最简单的方法,在PHP中用exec()或popen()函数将一个shell命令行推到后台去执行,例如:
 

代码示例:
<?php
exec("/bin/sh /opt/zhangyan.sh &");
?>

最后的&表示将shell脚本推到后台去执行。

但是这样会有一个问题,如果推到后台的进程太多,可能会导致服务器系统资源耗尽而崩溃,所以必须控制进程数量。

我写了一个PHP程序(/opt/zhangyan.php)、一个shell程序(/opt/zhangyan.sh)作为测试用例。

程序的逻辑:
  1、设置/opt/zhangyan.php最多允许生成500个子进程;
  2、当/opt/zhangyan.php读取到一条数据后,将允许生成的子进程数减1(空闲进程数$p_number=500-1=499),然后将数据交给/opt/zhangyan.sh去后台处理,不等待/opt/zhangyan.sh处理结束,继续读取下一条数据;
  3、当允许生成的子进程数减至0时(空闲进程数$p_number=0),/opt/zhangyan.php会等待1秒钟,然后检查后台还有多少个/opt/zhangyan.sh子进程尚未处理结束;
  4、如果1秒钟之后/opt/zhangyan.php发现后台的/opt/zhangyan.sh子进程数还是500(空闲进程数$p_number=0),会继续等待1秒钟,如此反复;
  5、如果/opt/zhangyan.php发现后台尚未处理结束的/opt/zhangyan.sh子进程数减少到300个了(空闲进程数$p_number=500-300=200),那么/opt/zhangyan.php会再往后台推送200个/opt/zhangyan.sh子进程;


/opt/zhangyan.php代码如下:
 

代码示例:
    <?php 
    function run($input) 
    { 
        global $p_number; 
        if ($p_number <= 0) 
        { 
            $p_number = worker_processes($p_number); 
        } 
        $p_number = $p_number - 1; 
        $out = popen("/bin/sh /opt/zhangyan.sh \"{$input}\" &", "r"); 
        pclose($out); 
    } 
     
    function worker_processes($p_number) 
    { 
        $limit = 500;//允许推到后台的最大进程数 
        while ($p_number <= 0) 
        { 
            $cmd = popen("ps -ef | grep \"/opt/zhangyan.sh\" | grep -v grep | wc -l", "r"); 
            $line = fread($cmd, 512); 
            pclose($cmd); 
            $p_number = $limit - $line; 
            if ($p_number <= 0) 
            { 
                sleep(1);//暂停1秒钟 
            } 
        } 
        return $p_number; 
    } 
     
    $input = "http://www."; //模拟从队列文件中读取到的数据 
    for ($i = 1; $i <= 1000; $i++) 
    { 
        run($input); 
        echo "Idle process number: " . $p_number . "\n"; 
    } 
    ?>

 (/opt/zhangyan.php程序用来模拟从队列文件中读取1000行数据,交给子进程/opt/zhangyan.sh去处理。)


/opt/zhangyan.sh代码如下:
 

代码示例:
#!/bin/sh 
    echo $(date -d "today" +"%Y-%m-%d %H:%M:%S") $1 >> /opt/zhangyan.log 
    sleep_time=$(expr $RANDOM % 4 + 1) 
    sleep $sleep_time 

  (/opt/zhangyan.sh脚本用来模拟向外地接收服务器发送数据。其中的$(expr $RANDOM % 4 + 1)用来生成1~5之间的随机数,用来使程序暂停1~5秒钟。暂停1秒表示网络状况好,发送数据顺畅;暂停2~6秒表示网络状况不好,发送过程需要1~5秒。)

执行程序:
 

代码示例:
/usr/local/php/bin/php /opt/zhangyan.php
(/usr/local/php/bin/php因PHP解析器所在的路径)

 查看/opt/zhangyan.sh打下的日志文件的第一行和最后一行:
 

代码示例:
head -n 1 /opt/zhangyan.log
 2007-11-16 07:54:13 http://www.
tail -n 1 /opt/zhangyan.log
 2007-11-16 07:54:18 http://www.

  可以看出,500进程并发处理这1000条数据只耗费5秒钟。而按照原来的串行模式,处理每条数据即使只耗费最短的1秒钟,也需要1000秒,约合16分钟才能完成。
备注:将PHP程序作为Linux守护进程的方法:
 

代码示例:
nohup /usr/local/php/bin/php /opt/zhangyan.php 2>&1 > /dev/null &

(nohup命令可以在用户退出终端后仍然执行程序,“2>&1 > /dev/null”表示不显示标准输出和错误输出,最后的&表示推到后台执行。)


    
最新技术文章:
▪PHP函数microtime()时间戳的定义与用法
▪PHP单一入口之apache配置内容
▪PHP数组排序方法总结(收藏)
▪php数组排序方法大全(脚本学堂整理奉献)
php开源软件 iis7站长之家
▪php二维数组排序(实例)
▪php根据键值对二维数组排序的小例子
▪php验证码(附截图)
▪php数组长度的获取方法(三个实例)
▪php获取数组长度的方法举例
▪判断php数组维度(php数组长度)的方法
▪php获取图片的exif信息的示例代码
▪PHP 数组key长度对性能的影响实例分析
▪php函数指定默认值的方法示例
▪php提交表单到当前页面、提交表单后页面重定...
▪php四舍五入的三种实现方法
▪php获得数组长度(元素个数)的方法
▪php日期函数的简单示例代码
▪php数学函数的简单示例代码
▪php字符串函数的简单示例代码
▪php文件下载代码(多浏览器兼容、支持中文文...
▪php实现文件下载、支持中文文件名的示例代码...
▪php文件下载(防止中文文件名乱码)的示例代码
▪解决PHP文件下载时中文文件名乱码的问题
▪php数组去重(一维、二维数组去重)的简单示例
▪php小数点后取两位的三种实现方法
▪php Redis 队列服务的简单示例
▪PHP导出excel时数字变为科学计数的解决方法
▪PHP数组根据值获取Key的简单示例
▪php数组去重的函数代码示例
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3