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 相关文章推荐
我的群发邮件程序
Oct 09 PHP
PHP 5.3新特性命名空间规则解析及高级功能
Mar 11 PHP
解析php做推送服务端实现ios消息推送
Jul 01 PHP
php实现refresh刷新页面批量导入数据的方法
Dec 23 PHP
php include类文件超时问题处理
Feb 06 PHP
PHP输入输出流学习笔记
May 12 PHP
图文详解phpstorm配置Xdebug进行调试PHP教程
Jun 13 PHP
PHP preg_match实现正则表达式匹配功能【输出是否匹配及匹配值】
Jul 19 PHP
php 数据结构之链表队列
Oct 17 PHP
laravel框架模板之公共模板、继承、包含实现方法分析
Aug 30 PHP
Laravel 中使用简单的方法跟踪用户是否在线(推荐)
Oct 30 PHP
VSCode+PHPstudy配置PHP开发环境的步骤详解
Aug 20 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抓取页面与代码解析 推荐
2010/07/23 PHP
php curl模拟post请求和提交多维数组的示例代码
2015/11/19 PHP
php自定义扩展名获取函数示例
2016/12/12 PHP
thinkPHP5框架实现基于ajax的分页功能示例
2018/06/12 PHP
JavaScript实际应用:innerHTMl和确认提示的使用
2006/06/22 Javascript
简明json介绍
2008/09/28 Javascript
jquery多浏览器捕捉回车事件代码
2010/06/22 Javascript
javascript中的delete使用详解
2013/04/11 Javascript
JS中数组Array的用法示例介绍
2014/02/20 Javascript
jQuery对JSON数据进行排序输出的方法
2015/06/24 Javascript
在JavaScript中对HTML进行反转义详解
2016/05/18 Javascript
jQuery的extend方法【三种】
2016/12/14 Javascript
微信小程序 页面之间传参实例详解
2017/01/13 Javascript
jQuery插件之validation插件
2017/03/29 jQuery
JS实现移动端触屏拖拽功能
2018/07/31 Javascript
对vue中v-if的常见使用方法详解
2018/09/28 Javascript
vue 实现移动端键盘搜索事件监听
2019/11/06 Javascript
React学习之受控组件与数据共享实例分析
2020/01/06 Javascript
uni-app实现获取验证码倒计时功能
2020/11/01 Javascript
Python中*args和**kwargs的区别详解
2019/09/17 Python
django 简单实现登录验证给你
2019/11/06 Python
html5中canvas学习笔记1-画板的尺寸与实际显示尺寸
2013/01/06 HTML / CSS
HTML5新增加的功能详解
2016/09/05 HTML / CSS
Crocs美国官方网站:卡骆驰洞洞鞋
2017/08/04 全球购物
美国一家运动专业鞋类零售商:Warehouse Shoe Sale(WSS)
2018/03/28 全球购物
如何保障Web服务器安全
2014/05/05 面试题
幼儿园保育员辞职信
2014/01/12 职场文书
八项规定整改方案
2014/02/21 职场文书
促销活动方案模板
2014/02/24 职场文书
法学专业毕业生求职信
2014/06/12 职场文书
2014年售票员工作总结
2014/11/19 职场文书
二年级语文上册复习计划
2015/01/19 职场文书
长江三峡导游词
2015/01/31 职场文书
十七岁的单车观后感
2015/06/12 职场文书
python 实现体质指数BMI计算
2021/05/26 Python
JavaScript分页组件使用方法详解
2021/07/26 Javascript