侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

深入探究PHP的多进程编程方法

php  /  管理员 发布于 7年前   181

子进程的创建
一般的子进程的写法是:

    上边的代码如果创建子进程成功的话,系统就有了2个进程,一个为父进程,一个为子进程,子进程的id号为$pid。在系统运行到$pid = pcntl_fork();时,在这个地方进行分支,父子进程各自开始运行各自的程序代码。代码的运行结果是parent 和child,很奇怪吧,为什么一个if和else互斥的代码中,都输出了结果?其实是像上边所说的,代码在pcntl_fork时,一个父进程运行parent,一个子进程运行了child。在代码结果上就显示了parent和child。至于谁先谁后的问题,这得要看系统资源的分配了。

    如果需要起多个进程来处理数据,可以根据数据的数量,按照约定好的数量比如说1000条一个进程来起子进程。使用for循环就可以了。   

 #如果获得的总数小于或等于0,等待60秒,并退出  if ($count <= 0)   {    sleep(60);    exit;  }  #如果大于1000,计算需要起的进程数  if ($count > 1000)  {    $cycleSize = ceil($count/1000);  }  else  {    $cycleSize = 1;  }    for ($i=0; $i<$cycleSize; $i++)  {    $pid  = pcntl_fork();    if($pid == -1)    {      break;    }    else    {      if($pid)      {        #父进程获得子进程的pid,存入数组        $pidArr[] = $pid;      }      else      {        //开始发送,子进程执行完自己的任务后,退出。          exit;      }    }  }    while(count($pidArr) > 0)  {    $myId  = pcntl_waitpid(-1, $status, WNOHANG);    foreach($pidArr as $key => $pid)    {      if($myId == $pid) unset($pidArr[$key]);    }  }

    然后使用crontab,来使此PHP程序每隔一段时间自动执行。

    当然,示例代码比较简单,具体还需要考虑怎么防止多个子进程执行到同一条数据或者当前进程处理数据未完成时,crontab又开始执行PHP文件启用新的进程等等。


PHP多进程实现方式
下面来系统地整理一下PHP多进程的实现方式:

1. 直接方式

pcntl_fork() 创建一个进程,在父进程返回值是子进程的pid,在子进程返回值是0,-1表示创建进程失败。跟C非常相似。

测试脚本 test.php
 

 0){         echo "parent continue \n";         for ($k=0; $k<2; ++$k){           beep();        }     } else if ($pid == 0){         echo "child start, pid ", getmypid(), "\n" ;         for ($j=0; $j<5; ++$j){           beep();        }         exit ;     }  }  // ***  function beep(){      echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;     sleep(1);  }?>

用命令行运行

#php -f test.php

输出结果

parent start, pid 17931793  2013-01-14 15:04:17parent continue1793  2013-01-14 15:04:18child start, pid 17941794  2013-01-14 15:04:181794  2013-01-14 15:04:191793  2013-01-14 15:04:191794  2013-01-14 15:04:20parent continue1793  2013-01-14 15:04:20child start, pid 17951795  2013-01-14 15:04:2017931794        2013-01-14 15:04:212013-01-14 15:04:211795  2013-01-14 15:04:211794  2013-01-14 15:04:221795  2013-01-14 15:04:22parent continue1793  2013-01-14 15:04:22child start, pid 17961796  2013-01-14 15:04:221793  2013-01-14 15:04:231796  2013-01-14 15:04:231795  2013-01-14 15:04:231795  2013-01-14 15:04:241796  2013-01-14 15:04:241796  2013-01-14 15:04:251796  2013-01-14 15:04:26

从中看到,创建了3个子进程,和父进程一起并行运行。其中有一行格式跟其他有些不同,
17931794                2013-01-14 15:04:212013-01-14 15:04:21
因为两个进程同时进行写操作,造成了冲突。


2. 阻塞方式

用直接方式,父进程创建了子进程后,并没有等待子进程结束,而是继续运行。似乎这里看不到有什么问题。如果php脚本并不是运行完后自动结束,而是常驻内存的,就会造成子进程无法回收的问题。也就是僵尸进程。可以通过pcntl_wai()方法等待进程结束,然后回收已经结束的进程。
将测试脚本改成:
 

$pid = pcntl_fork();if ($pid == -1){  ...} else if ($pid > 0){   echo "parent continue \n";   pcntl_wait($status);   for ($k=0; $k<2; ++$k){     beep();  }} else if ($pid == 0){   ...}

用命令行运行

#php -f test.php

输出结果

parent start, pid 18071807  2013-01-14 15:20:05parent continuechild start, pid 18081808  2013-01-14 15:20:061808  2013-01-14 15:20:071808  2013-01-14 15:20:081808  2013-01-14 15:20:091808  2013-01-14 15:20:101807  2013-01-14 15:20:111807  2013-01-14 15:20:12parent continuechild start, pid 18091809  2013-01-14 15:20:131809  2013-01-14 15:20:141809  2013-01-14 15:20:151809  2013-01-14 15:20:161809  2013-01-14 15:20:171807  2013-01-14 15:20:181807  2013-01-14 15:20:19child start, pid 18101810  2013-01-14 15:20:20parent continue1810  2013-01-14 15:20:211810  2013-01-14 15:20:221810  2013-01-14 15:20:231810  2013-01-14 15:20:241807  2013-01-14 15:20:251807  2013-01-14 15:20:26

父进程在pcntl_wait()将自己阻塞,等待子进程运行完了才接着运行。


3. 非阻塞方式

阻塞方式失去了多进程的并行性。还有一种方法,既可以回收已经结束的子进程,又可以并行。这就是非阻塞的方式。
修改脚本:
 

 0){         echo "parent continue \n";         for ($k=0; $k<2; ++$k){           beep();        }     } else if ($pid == 0){         echo "child start, pid ", getmypid(), "\n" ;         for ($j=0; $j<5; ++$j){           beep();        }         exit (0);     }  }  // parent  while (1){      // do something else     sleep(5);  }  // ***  function garbage($signal){      echo "signel $signal received\n" ;while (($pid = pcntl_waitpid(-1, $status, WNOHANG))> 0){         echo "\t child end pid $pid , status $status\n" ;     }  }  function beep(){      echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;     sleep(1);  }?>

用命令行运行

#php -f test.php &

输出结果

parent start, pid 20662066  2013-01-14 16:45:34parent continue2066  2013-01-14 16:45:35child start, pid 20672067  2013-01-14 16:45:3520662067        2013-01-14 16:45:362013-01-14 16:45:362067  2013-01-14 16:45:37parent continue2066  2013-01-14 16:45:37child start, pid 20682068  2013-01-14 16:45:372067  2013-01-14 16:45:382068  2013-01-14 16:45:382066  2013-01-14 16:45:38parent continue2066  2013-01-14 16:45:40child start, pid 20692069  2067  2013-01-14 16:45:402013-01-14 16:45:402068  2013-01-14 16:45:402066  2013-01-14 16:45:412069  2013-01-14 16:45:412068  2013-01-14 16:45:41signel 17 received     child end pid 2067, status 02069  2013-01-14 16:45:422068  2013-01-14 16:45:422069  2013-01-14 16:45:43signel 17 received     child end pid 2068, status 02069  2013-01-14 16:45:44signel 17 received     child end pid 2069, status 0

多个进程又并行运行了,而且运行大约10秒钟之后,用 ps -ef | grep php 查看正在运行的进程,只有一个进程
lqling    2066  1388  0 16:45 pts/1    00:00:00 php -f t5.php
是父进程,子进程被回收了。


子进程退出状态

pcntl_waitpid(-1, $status, WNOHANG) $status

 返回子进程的结束状态


windows下多线程

windows系统不支持pcntl函数,幸好有curl_multi_exec()这个工具,利用内部的多线程,访问多个链接,每个链接可以作为一个任务。

编写脚本 test1.php
 

 $task){     $ch[$i] = curl_init();     curl_setopt($ch[$i], CURLOPT_URL, $task);     curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);     curl_multi_add_handle($mh, $ch[$i]);  }  do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM);  while ($active && $mrc == CURLM_OK) {     if (curl_multi_select($mh) != -1) {      do {$mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM);     }  }  // completed, checkout result  foreach ($tasks as $j => $task){     if (curl_error($ch[$j])){       echo "task ${j} [$task ] error " , curl_error($ch[$j]), "\r\n" ;     } else {       echo "task ${j} [$task ] get: \r\n" , curl_multi_getcontent($ch[$j]), "\r\n" ;     }  }?>

编写脚本 test2.php
 

用命令行运行

#php -f test1.php &

输出结果

task 0 [http://localhost/feedbowl/t2.php?job=task1] get:child start, pid 58045804  2013-01-15 20:22:355804  2013-01-15 20:22:365804  2013-01-15 20:22:375804  2013-01-15 20:22:385804  2013-01-15 20:22:39task 1 [http://localhost/feedbowl/t2.php?job=task2] get:child start, pid 58045804  2013-01-15 20:22:355804  2013-01-15 20:22:365804  2013-01-15 20:22:375804  2013-01-15 20:22:385804  2013-01-15 20:22:39task 2 [http://localhost/feedbowl/t2.php?job=task3] get:child start, pid 58045804  2013-01-15 20:22:355804  2013-01-15 20:22:365804  2013-01-15 20:22:375804  2013-01-15 20:22:385804  2013-01-15 20:22:39

从打印的时间看到,多个任务几乎是同时运行的。

您可能感兴趣的文章:

  • 深入解析PHP中的(伪)多线程与多进程
  • PHP多进程编程实例
  • PHP多进程之pcntl_fork的实例详解
  • PHP 多进程 解决难题
  • 解析PHP实现多进程并行执行脚本
  • php中实现进程锁与多进程的方法
  • php开启多进程的方法
  • php中pcntl_fork创建子进程的方法实例
  • 用php守护另一个php进程的例子
  • php 多进程编程父进程的阻塞与非阻塞实例分析


  • 上一条:
    以实例全面讲解PHP中多进程编程的相关函数的使用
    下一条:
    PHP的命令行命令使用指南
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客