php 的多进程操作实践案例分析


Posted in PHP onFebruary 28, 2020

本文实例讲述了php 的多进程操作。分享给大家供大家参考,具体如下:

php的多进程处理依赖于pcntl扩展,通过pcntl_fork创建子进程来进行并行处理。

例1如下:

<?php
$pid = pcntl_fork();

if($pid == -1) {
  //错误处理:创建子进程失败时返回-1.
  die('fork error');
} else if ($pid) {
  //父进程会得到子进程号,所以这里是父进程执行的逻辑
  echo "parent \n";
  //等待子进程中断,防止子进程成为僵尸进程。
  pcntl_wait($status);
} else {
  //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
  echo "child \n";

  exit;
}

pcntl_fork创建了子进程,父进程和子进程都继续向下执行,而不同是父进程会获取子进程的$pid也就是$pid不为零。而子进程会获取$pid为零。通过if else语句判断$pid我们就可以在指定位置写上不同的逻辑代码。

上述代码会分别输出parent和child。那么输出的parent和child是否会有顺序之分?是父进程会先执行?

例2如下:

<?php
$pid = pcntl_fork();

if($pid == -1) {
  die('fork error');
} else if ($pid) {
  sleep(3);
  echo "parent \n";
  pcntl_wait($status);
} else {
  echo "child \n";

  exit;
}

我们在父进程中通过sleep来延缓执行,看看效果。

结果是,很快输出了child,等待了接近3秒后,才输出parent。所以父进程和子进程的执行是相对独立的,没有先后之分。

那么问题又来了?pcntl_wait是做什么用的?
会挂起当前进程,直到子进程退出,如果子进程在调用此函数之前就已退出,此函数会立刻返回。子进程使用的资源将被释放。

例3如下:

<?php
$pid = pcntl_fork();

if($pid == -1) {
  die('fork error');
} else if ($pid) {
  pcntl_wait ($status);
  echo "parent \n";
} else {
  sleep(3);
  echo "child \n";

  exit;
}

上述代码,我们可以看到,父进程执行pcntl_wait时就已经挂起,直到等待3秒后输出child,子进程退出后。父进程继续执行,输出parent。

例4如下:

<?php
define('FORK_NUMS', 3);

$pids = array();

for($i = 0; $i < FORK_NUMS; ++$i) {
  $pids[$i] = pcntl_fork();
  if($pids[$i] == -1) {
    die('fork error');
  } else if ($pids[$i]) {
    pcntl_waitpid($pids[$i], $status);
    echo "pernet \n";
  } else {
    sleep(3);
    echo "child id:" . getmypid() . " \n";
    exit;
  }
}

上述代码,我们创建3个子进程,父进程分别挂起等待子进程结束后,输出parent。

输出结果如下:

child id:19090
pernet
child id:19091
pernet
child id:19092
pernet

例5如下:

<?php
define('FORK_NUMS', 3);

$pids = array();

for($i = 0; $i < FORK_NUMS; ++$i) {
  $pids[$i] = pcntl_fork();
  if($pids[$i] == -1) {
    die('fork error');
  } else if ($pids[$i]) {

  } else {
    sleep(3);
    echo "child id:" . getmypid() . " \n";
    exit;
  }
}

foreach($pids as $k => $v) {
  if($v) {
    pcntl_waitpid($v, $status);
    echo "parent \n";
  }
}

输出结果如下:

child id:19118
child id:19119
child id:19120
parent
parent
parent

为什么上述代码跟例4的输出结果不一样?

我们可以看到例5的pcntl_waitpid函数放在了foreach中,foreach代码是在主进程中,也就是父进程的代码中。当执行foreach时,可能子进程已经全部执行完毕并退出。pcntl_waitpid会立刻返回,连续输出三个parent。

(*在子进程中,需通过exit来退出,不然会产生递归多进程,父进程中不需要exit,不然会中断多进程。)

例6如下:

<?php
define('FORK_NUMS', 3);

$pids = array();

$fp = fopen('./test.log', 'wb');
$num = 1;

for($i = 0; $i < FORK_NUMS; ++$i) {
  $pids[$i] = pcntl_fork();
  if($pids[$i] == -1) {
    die('fork error');
  } else if ($pids[$i]) {


  } else {
    for($i = 0; $i < 5; ++$i) {

      flock($fp, LOCK_EX);
      fwrite($fp, getmypid() . ' : ' . date('Y-m-d H:i:s') . " : {$num} \r\n");

      flock($fp, LOCK_UN);
      echo getmypid(), ": success \r\n";
      ++$num;
    }
    exit;
  }
}

foreach($pids as $k => $v) {
  if($v) {
    pcntl_waitpid($v, $status);
  }
}

fclose($fp);

代码如上:我们创建三个子进程,来同时向test.log文件写入内容,test.log内容如下:

19507 : 2016-03-16 20:40:52 : 1
19507 : 2016-03-16 20:40:52 : 2
19507 : 2016-03-16 20:40:52 : 3
19507 : 2016-03-16 20:40:52 : 4
19507 : 2016-03-16 20:40:52 : 5
19509 : 2016-03-16 20:40:52 : 1
19509 : 2016-03-16 20:40:52 : 2
19509 : 2016-03-16 20:40:52 : 3
19509 : 2016-03-16 20:40:52 : 4
19509 : 2016-03-16 20:40:52 : 5
19508 : 2016-03-16 20:40:52 : 1
19508 : 2016-03-16 20:40:52 : 2
19508 : 2016-03-16 20:40:52 : 3
19508 : 2016-03-16 20:40:52 : 4
19508 : 2016-03-16 20:40:52 : 5

我们可以看到三个子进程的pid,它们分别执行了5次,时间几乎是在同时。但是$num的值并没像我们期望的那样从1-15进行递增。子进程中的变量是各自独立的,互不影响。子进程会自动复制父进程空间里的变量。

如何在进程中共享数据?

我们通过php的共享内存函数shmop来实现。

<?php
define('FORK_NUMS', 3);

$pids = array();

$fp = fopen('./test.log', 'wb');
$num = 1;
//共享内存段的key
$shmKey = 123;
//创建共享内存段
$shmId = shmop_open($shmKey, 'c', 0777, 64);
//写入数据到共享内存段
shmop_write($shmId, $num, 0);

for($i = 0; $i < FORK_NUMS; ++$i) {
  $pids[$i] = pcntl_fork();
  if($pids[$i] == -1) {
    die('fork error');
  } else if ($pids[$i]) {

    //阻塞,等待子进程退出

    //注意这里,如果是非阻塞的话,$num的计数会出现问题。
    pcntl_waitpid($pids[$i], $status);
  } else {
    //读取共享内存段中的数据
    $num = shmop_read($shmId, 0, 64);
    for($i = 0; $i < 5; ++$i) {
      fwrite($fp, getmypid() . ' : ' . date('Y-m-d H:i:s') . " : {$num} \r\n");
      echo getmypid(), ": success \r\n";
      //递增$num
      $num = intval($num) + 1;
    }

    //写入到共享内存段中

    shmop_write($shmId, $num, 0);
    exit;
  }
}

//shmop_delete不会实际删除该内存段,它将该内存段标记为删除。
shmop_delete($shmId);
shmop_close($shmId);
fclose($fp);

上述代码的运行结果如下:

19923 : 2016-03-17 00:05:18 : 1
19923 : 2016-03-17 00:05:18 : 2
19923 : 2016-03-17 00:05:18 : 3
19923 : 2016-03-17 00:05:18 : 4
19923 : 2016-03-17 00:05:18 : 5
19924 : 2016-03-17 00:05:18 : 6
19924 : 2016-03-17 00:05:18 : 7
19924 : 2016-03-17 00:05:18 : 8
19924 : 2016-03-17 00:05:18 : 9
19924 : 2016-03-17 00:05:18 : 10
19925 : 2016-03-17 00:05:18 : 11
19925 : 2016-03-17 00:05:18 : 12
19925 : 2016-03-17 00:05:18 : 13
19925 : 2016-03-17 00:05:18 : 14
19925 : 2016-03-17 00:05:18 : 15

这样我们就在进程间共享了$num的数据。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
附件名前加网站名
Mar 23 PHP
phpmail类发送邮件函数代码
Feb 20 PHP
如何在smarty中增加类似foreach的功能自动加载数据
Jun 26 PHP
php定义数组和使用示例(php数组的定义方法)
Mar 29 PHP
php快递单号查询接口使用示例
May 05 PHP
PHP动态柱状图实现方法
Mar 30 PHP
PHP实现生成唯一会员卡号
Aug 24 PHP
谈谈PHP连接Access数据库的注意事项
Aug 12 PHP
PHP严重致命错误处理:php Fatal error: Cannot redeclare class or function
Feb 05 PHP
PHP排序算法之堆排序(Heap Sort)实例详解
Apr 21 PHP
php微信开发之关注事件
Jun 14 PHP
Laravel5.4框架中视图共享数据的方法详解
Sep 05 PHP
php 下 html5 XHR2 + FormData + File API 上传文件操作实例分析
Feb 28 #PHP
php的无刷新操作实现方法分析
Feb 28 #PHP
php下的原生ajax请求用法实例分析
Feb 28 #PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
Feb 27 #PHP
php使用fputcsv实现大数据的导出操作详解
Feb 27 #PHP
gearman中任务的优先级和返回状态实例分析
Feb 27 #PHP
gearman中worker常驻后台,导致MySQL server has gone away的解决方法
Feb 27 #PHP
You might like
PHP 表单提交给自己
2008/07/24 PHP
解析PHP中VC6 X86和VC9 X86的区别及 Non Thread Safe的意思
2013/06/28 PHP
php和redis实现秒杀活动的流程
2019/07/17 PHP
Javascript 表单之间的数据传递代码
2008/12/04 Javascript
基于jQuery制作迷你背词汇工具
2010/07/27 Javascript
基于jQuery的合并表格中相同文本的相邻单元格的代码
2011/04/06 Javascript
JavaScript中获取未知对象属性的代码
2011/04/27 Javascript
JavaScript中的onerror事件概述及使用
2013/04/01 Javascript
JavaScript中双向数据绑定详解
2017/05/03 Javascript
JS禁止浏览器右键查看元素或按F12审查元素自动关闭页面示例代码
2017/09/07 Javascript
Vue中的v-for循环key属性注意事项小结
2018/08/12 Javascript
通过vue-cli3构建一个SSR应用程序的方法
2018/09/13 Javascript
react写一个select组件的实现代码
2019/04/03 Javascript
浅谈Express.js解析Post数据类型的正确姿势
2019/05/30 Javascript
Vue.js数字输入框组件使用方法详解
2019/10/19 Javascript
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
Node.js Domain 模块实例详解
2020/03/18 Javascript
Vue.js中使用Vuex实现组件数据共享案例
2020/07/31 Javascript
原生JS实现九宫格抽奖
2020/09/13 Javascript
解决element-ui的下拉框有值却无法选中的情况
2020/11/07 Javascript
python中lambda与def用法对比实例分析
2015/04/30 Python
详解Python with/as使用说明
2018/12/13 Python
十行代码使用Python写一个USB病毒
2019/06/21 Python
Python秒算24点实现及原理详解
2019/07/29 Python
python 浅谈serial与stm32通信的编码问题
2019/12/18 Python
基于HTML5的WebSocket的实例代码
2018/08/15 HTML / CSS
护理专业学生的求职信范文
2013/12/11 职场文书
培训主管岗位职责
2014/02/01 职场文书
王兆力在市委党的群众路线教育实践活动总结大会上的讲话稿
2014/10/25 职场文书
普通党员个人整改措施
2014/10/27 职场文书
2015年敬老院工作总结
2015/05/18 职场文书
任命书格式范文
2015/09/22 职场文书
2015年社区消防安全工作总结
2015/10/14 职场文书
python批量创建变量并赋值操作
2021/06/03 Python
python使用torch随机初始化参数
2022/03/22 Python
利用uni-app生成微信小程序的踩坑记录
2022/04/05 Javascript