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 相关文章推荐
PHP中获取文件扩展名的N种方法小结
Feb 27 PHP
标准PHP的AES加密算法类
Mar 12 PHP
如何实现php图片等比例缩放
Jul 28 PHP
如何写php守护进程(Daemon)
Dec 30 PHP
YII Framework框架教程之安全方案详解
Mar 14 PHP
PHP基于DOMDocument解析和生成xml的方法分析
Jul 17 PHP
PHP实现求连续子数组最大和问题2种解决方法
Dec 26 PHP
PHP实现的超长文本分页显示功能示例
Jun 04 PHP
PHP基础之输出缓冲区基本概念、原理分析
Jun 19 PHP
PHPExcel实现的读取多工作表操作示例
Apr 14 PHP
Laravel如何实现适合Api的异常处理响应格式
Jun 14 PHP
PHP之header函数详解
Mar 02 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
利用文件属性结合Session实现在线人数统计
2006/10/09 PHP
laravel批量生成假数据的方法
2019/10/09 PHP
零基础学JavaScript最新动画教程+iso光盘下载
2008/01/22 Javascript
jQuery 学习 几种常用方法
2009/06/11 Javascript
Web Inspector:关于在 Sublime Text 中调试Js的介绍
2013/04/18 Javascript
JS保留两位小数,多位小数的示例代码
2014/01/07 Javascript
jquery实现邮箱自动补全功能示例分享
2014/02/17 Javascript
详解AngularJS中的表格使用
2015/06/16 Javascript
解决wx.onMenuShareTimeline出现的问题
2016/08/16 Javascript
js原生跨域_用script标签的简单实现
2016/09/24 Javascript
详解js前端代码异常监控
2017/01/11 Javascript
socket.io学习教程之基本应用(二)
2017/04/29 Javascript
jquery拖拽自动排序插件使用方法详解
2020/07/20 jQuery
vue实现购物车小案例
2019/09/27 Javascript
通过实例解析javascript Date对象属性及方法
2020/11/04 Javascript
python ElementTree 基本读操作示例
2009/04/09 Python
编写简单的Python程序来判断文本的语种
2015/04/07 Python
详解python中字典的循环遍历的两种方式
2017/02/07 Python
基于Django模板中的数字自增(详解)
2017/09/05 Python
python命名空间(namespace)简单介绍
2019/08/10 Python
Python3和PyCharm安装与环境配置【图文教程】
2020/02/14 Python
Java Spring项目国际化(i18n)详细方法与实例
2020/03/20 Python
Python中对象的比较操作==和is区别详析
2021/02/12 Python
前端实现背景虚化但内容清晰且自适应 的实例代码
2019/08/01 HTML / CSS
UGG雪地靴德国官网:UGG德国
2016/11/19 全球购物
美国浴缸、水槽和水龙头购物网站:Vintage Tub & Bath
2019/11/05 全球购物
九年级英语教学反思
2014/01/31 职场文书
和谐家庭演讲稿
2014/05/24 职场文书
学校安全防火方案
2014/06/07 职场文书
2014年维修工作总结
2014/11/22 职场文书
工作表扬信范文
2015/01/17 职场文书
新生儿未入户证明
2015/06/23 职场文书
丧事主持词
2015/07/02 职场文书
运动会闭幕式通讯稿
2015/07/18 职场文书
python如何查找列表中元素的位置
2022/05/30 Python
Redis特殊数据类型Geospatial地理空间
2022/06/01 Redis