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 中dirname(_file_)讲解
Mar 18 PHP
使用PHP socke 向指定页面提交数据
Jul 23 PHP
解析PHP缓存函数的使用说明
May 10 PHP
thinkphp的c方法使用示例
Feb 24 PHP
ThinkPHP实现支付宝接口功能实例
Dec 02 PHP
PHP中if和or运行效率对比
Dec 12 PHP
php+mysqli使用面向对象方式更新数据库实例
Jan 29 PHP
php禁用函数设置及查看方法详解
Jul 25 PHP
php实现博客,论坛图片防盗链的方法
Oct 15 PHP
php简单中奖算法(实例)
Aug 15 PHP
PHP实现基于栈的后缀表达式求值功能
Nov 10 PHP
解决在laravel中auth建立时候遇到的问题
Oct 15 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 生成WML页面方法详解
2009/08/09 PHP
php设置编码格式的方法
2013/03/05 PHP
php新浪微博登录接口用法实例
2014/12/23 PHP
PHP中SESSION的注销与清除
2015/04/16 PHP
thinkPHP5实现的查询数据库并返回json数据实例
2017/10/23 PHP
JS 页面内容搜索,类似于 Ctrl+F功能的实现代码
2007/08/13 Javascript
禁止JQuery中的load方法装载IE缓存中文件的方法
2009/09/11 Javascript
ExtJs使用IFrame的实现代码
2010/03/24 Javascript
JavaScript学习历程和心得小结
2010/08/16 Javascript
JS实现点击图片在当前页面放大并可关闭的漂亮效果
2013/10/18 Javascript
javascript 用函数语句和表达式定义函数的区别详解
2014/01/06 Javascript
JavaScript中的条件判断语句使用详解
2015/06/03 Javascript
DeviceOne 让你一见钟情的App快速开发平台
2016/02/17 Javascript
DIV+CSS+jQ实现省市联动可扩展
2016/06/22 Javascript
js 判断一个数字是不是2的n次方幂的实例
2017/11/26 Javascript
p5.js入门教程和基本形状绘制
2018/03/15 Javascript
微信小程序左滑删除实现代码实例
2019/09/16 Javascript
js使用文档就绪函数动态改变页面内容示例【innerHTML、innerText】
2019/11/07 Javascript
小程序实现按下录音松开识别语音
2019/11/22 Javascript
javascript代码实现简易计算器
2021/01/25 Javascript
Python的网络编程库Gevent的安装及使用技巧
2016/06/24 Python
Python实现多线程HTTP下载器示例
2017/02/11 Python
详解python的sorted函数对字典按key排序和按value排序
2018/08/10 Python
Python基础之文件读取的讲解
2019/02/16 Python
Python os.access()用法实例
2019/02/18 Python
python3通过selenium爬虫获取到dj商品的实例代码
2019/04/25 Python
Python操作列表常用方法实例小结【创建、遍历、统计、切片等】
2019/10/25 Python
python实现简单学生信息管理系统
2020/04/09 Python
Html5 postMessage实现跨域消息传递
2016/03/11 HTML / CSS
Desigual德国官网:在线购买原创服装
2018/03/27 全球购物
内部类的定义、种类以及优点
2013/10/16 面试题
工作疏忽、懈怠的检讨书
2014/09/11 职场文书
房产转让协议书(2014版)
2014/09/30 职场文书
怎样写离婚协议书
2015/01/26 职场文书
Java由浅入深通关抽象类与接口(下篇)
2022/04/26 Java/Android
python和C/C++混合编程之使用ctypes调用 C/C++的dll
2022/04/29 Python