CLI下另一种多进程实现方式----PCNTL

有些时候,你需要对一些脚本进行优化,以期跑的更快,在更短的时间内完成任务。PCNTL是一个不错的选择,它可以fork多个进程,来协同完成一个任务,理论上完成的时间将会和进程数成反比。

不过,PCNTL只能用于CLI模式下,不能用于Web服务器环境,否则可能会带来意料之外的错误,不推荐使用。

  • 安装PCNTL扩展:

    wget http://cn2.php.net/distributions/php-5.3.27.tar.bz2
    tar -jxvf php-5.3.27.tar.bz2
    cd php-5.3.27/ext/pcntl/
    phpize && ./configure && make install
    echo "extension=pcntl.so" >> /application/php5.3.27/lib/php.ini
    kill -USR2 `cat /var/run/php-fpm.pid`
    下载->解压->编译->添加扩展模块->重启php-fpm

    注意:线上web服务器中,PHP编译时已经集成该函数,不需要再单独添加,否则会报warning。

    • 程序实例:

      程序流程在注释中已经比较详细的说明了,这里不再单独叙述。

    • 执行结果:

      从结果中可以看出来,虽然程序中fork子进程是有序的,但是在实际执行中并不一定是有序的,进程完成的顺序与进程开始的顺序也不是保持一致的。
      fork前,子进程可以继承父进程的东西,但是在pcntl_fork()后子进程和父进程就没有任何继承关系了。在子进程里创建的东西是子进程的,在父进程创建的东西是父进程的,可以完全看成是两个独立的进程。
      fork后,程序出了分岔,派生出了两个进程,具体哪个先运行,哪个先结束就看该系统的调度算法了。

    • demo
    • <?php
      /**
       * pcntl test
       * @author: xxxx
       */
      
      set_time_limit(0);
      
      //如果找不到pcntl_fork函数,直接退出
      if (! function_exists('pcntl_fork')) echo "PCNTL functions not available on this PHP installation\n";
      
      //脚本运行开始
      $start =  time();
      echo "\nSCRIT RUN AT: ", date('Y-m-d H:i:s', $start), "\n";
      
      //从CLI取参数
      //默认跑10个进程
      $pmax = empty($argv[1]) ? 10 : $argv[1];
      //父进程pid
      $ppid = getmypid(); 
      for ($i = 1; $i <= $pmax; ++$i) {
          //开始产生子进程
          $pid = pcntl_fork();
          switch ($pid) {
          case -1:
              // fork失败
              echo "Fork failed!\n";
              break;
          case 0:
              // fork成功,并且子进程会进入到这里
              sleep(1);
              $cpid = getmypid(); //用getmypid()函数获取当前进程的PID
              echo "FORK: Child #{$i} #{$cpid} is running...\n";
              //子进程要exit否则会进行递归多进程,父进程不要exit否则终止多进程
              exit($i);
              break;
          default:
              // fork成功,并且父进程会进入到这里
              if ($i == 1) {
                  echo "Parent #{$ppid} is running...\n";
              }
              break;
         }
      }
      
      //父进程利用while循环,并且通过pcntl_waitpid函数来等待所有子进程完成后才继续向下进行
      while (pcntl_waitpid(0, $status) != -1) {
          //pcntl_wexitstatus返回一个中断的子进程的返回代码,由此可判断是哪一个子进程完成了
          $status = pcntl_wexitstatus($status); 
          echo "Child $status has completed!\n";
      }
      
      echo "Parent #{$ppid} has completed!\n";
      
      echo "\nSCRIT END AT: ", date('Y-m-d H:i:s', $start), "\n";
      echo "TOTAL TIMEEEE: " . (time() - $start)/60;
      echo "\n++++++++++++++++++++++++++++++++++++++++++++OK++++++++++++++++++++++++++++++++++++++++++++++++++\n";
      ?>

       

posted @ 2015-09-12 15:42  风与叶子  阅读(760)  评论(0编辑  收藏  举报