找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 271|回复: 0
打印 上一主题 下一主题

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

[复制链接]

2647

主题

2647

帖子

7881

积分

论坛元老

Rank: 8Rank: 8

积分
7881
跳转到指定楼层
楼主
发表于 2018-2-14 05:42:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

            子进程的创建
一般的子进程的写法是:
    上边的代码如果创建子进程成功的话,系统就有了2个进程,一个为父进程,一个为子进程,子进程的id号为$pid。在系统运行到$pid = pcntl_fork();时,在这个地方进行分支,父子进程各自开始运行各自的程序代码。代码的运行结果是parent 和child,很奇怪吧,为什么一个if和else互斥的代码中,都输出了结果?其实是像上边所说的,代码在pcntl_fork时,一个父进程运行parent,一个子进程运行了child。在代码结果上就显示了parent和child。至于谁先谁后的问题,这得要看系统资源的分配了。
    如果需要起多个进程来处理数据,可以根据数据的数量,按照约定好的数量比如说1000条一个进程来起子进程。使用for循环就可以了。   
#如果获得的总数小于或等于0,等待60秒,并退出
  if ($count  1000)
  {
    $cycleSize = ceil($count/1000);
  }
  else
  {
    $cycleSize = 1;
  }
  
  for ($i=0; $i 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
用命令行运行
#php -f test.php
输出结果
parent start, pid 1793
1793  2013-01-14 15:04:17
parent continue
1793  2013-01-14 15:04:18
child start, pid 1794
1794  2013-01-14 15:04:18
1794  2013-01-14 15:04:19
1793  2013-01-14 15:04:19
1794  2013-01-14 15:04:20
parent continue
1793  2013-01-14 15:04:20
child start, pid 1795
1795  2013-01-14 15:04:20
17931794        2013-01-14 15:04:212013-01-14 15:04:21
1795  2013-01-14 15:04:21
1794  2013-01-14 15:04:22
1795  2013-01-14 15:04:22
parent continue
1793  2013-01-14 15:04:22
child start, pid 1796
1796  2013-01-14 15:04:22
1793  2013-01-14 15:04:23
1796  2013-01-14 15:04:23
1795  2013-01-14 15:04:23
1795  2013-01-14 15:04:24
1796  2013-01-14 15:04:24
1796  2013-01-14 15:04:25
1796  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
用命令行运行
#php -f test.php
输出结果
parent start, pid 1807
1807  2013-01-14 15:20:05
parent continue
child start, pid 1808
1808  2013-01-14 15:20:06
1808  2013-01-14 15:20:07
1808  2013-01-14 15:20:08
1808  2013-01-14 15:20:09
1808  2013-01-14 15:20:10
1807  2013-01-14 15:20:11
1807  2013-01-14 15:20:12
parent continue
child start, pid 1809
1809  2013-01-14 15:20:13
1809  2013-01-14 15:20:14
1809  2013-01-14 15:20:15
1809  2013-01-14 15:20:16
1809  2013-01-14 15:20:17
1807  2013-01-14 15:20:18
1807  2013-01-14 15:20:19
child start, pid 1810
1810  2013-01-14 15:20:20
parent continue
1810  2013-01-14 15:20:21
1810  2013-01-14 15:20:22
1810  2013-01-14 15:20:23
1810  2013-01-14 15:20:24
1807  2013-01-14 15:20:25
1807  2013-01-14 15:20:26
父进程在pcntl_wait()将自己阻塞,等待子进程运行完了才接着运行。
3. 非阻塞方式
阻塞方式失去了多进程的并行性。还有一种方法,既可以回收已经结束的子进程,又可以并行。这就是非阻塞的方式。
修改脚本:

0){
         echo "parent continue \n";
         for ($k=0; $k 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 2066
2066  2013-01-14 16:45:34
parent continue
2066  2013-01-14 16:45:35
child start, pid 2067
2067  2013-01-14 16:45:35
20662067        2013-01-14 16:45:362013-01-14 16:45:36
2067  2013-01-14 16:45:37
parent continue
2066  2013-01-14 16:45:37
child start, pid 2068
2068  2013-01-14 16:45:37
2067  2013-01-14 16:45:38
2068  2013-01-14 16:45:38
2066  2013-01-14 16:45:38
parent continue
2066  2013-01-14 16:45:40
child start, pid 2069
2069  2067  2013-01-14 16:45:40
2013-01-14 16:45:40
2068  2013-01-14 16:45:40
2066  2013-01-14 16:45:41
2069  2013-01-14 16:45:41
2068  2013-01-14 16:45:41
signel 17 received
     child end pid 2067, status 0
2069  2013-01-14 16:45:42
2068  2013-01-14 16:45:42
2069  2013-01-14 16:45:43
signel 17 received
     child end pid 2068, status 0
2069  2013-01-14 16:45:44
signel 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 5804
5804  2013-01-15 20:22:35
5804  2013-01-15 20:22:36
5804  2013-01-15 20:22:37
5804  2013-01-15 20:22:38
5804  2013-01-15 20:22:39
task 1 [http://localhost/feedbowl/t2.php?job=task2] get:
child start, pid 5804
5804  2013-01-15 20:22:35
5804  2013-01-15 20:22:36
5804  2013-01-15 20:22:37
5804  2013-01-15 20:22:38
5804  2013-01-15 20:22:39
task 2 [http://localhost/feedbowl/t2.php?job=task3] get:
child start, pid 5804
5804  2013-01-15 20:22:35
5804  2013-01-15 20:22:36
5804  2013-01-15 20:22:37
5804  2013-01-15 20:22:38
5804  2013-01-15 20:22:39
从打印的时间看到,多个任务几乎是同时运行的。
            
            
您可能感兴趣的文章:
  • PHP flock 文件锁详细介绍
  • PHP文件锁定写入实例解析
  • PHP多进程编程实例
  • PHP文件锁函数flock()详细介绍
  • PHP对文件进行加锁、解锁实例
  • PHP session文件独占锁引起阻塞问题解决方法
  • PHP使用flock实现文件加锁的方法
  • PHP程序中的文件锁、互斥锁、读写锁使用技巧解析
  • 简单谈谈 php 文件锁
  • PHP实现对文件锁进行加锁、解锁操作的方法
  • PHP 文件锁与进程锁的使用示例
  • PHP基于文件锁解决多进程同时读写一个文件问题示例
            
  • 分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    用户反馈
    客户端