php多进程-管道

php多进程-管道

上一篇提到,使用php多进程来加速运算不方便汇总子进程的数据问题。

第一种:
各个子进程将运行结果写入临时文件,主进程读取然后汇总,可实现,但是比较麻烦。

第二种:
使用进程间通讯工具(IPC):管道,命名管道,php消息队列sysvmsg,共享内存,Unix Socket,Redis,MQ等

管道特性:
1、管道的优点是不需要加锁,一次只能有一个用户写入。
2、默认缓冲区太小,只有4K。
3、同时只适合父子进程间通信,而且一个管道只适合单向通信,如果要双向通信需要建立两个。
4、它的发送接收机制是用read/write这种适用流的,缺点是数据本身没有边界,需要应用程序自己解释,而一般消息大多是一个固定
长的消息头,和一个变长的消息体,一个子进程从管道read到消息头后,消息体可能被别的子进程接收到。
5、后写入的信息不会覆盖前面的信息,而是在管道里按先后排队,可以看到file_put_contents并不用加 FILE_APPEND。
6、管道分为无名管道PIPE(非永久 的)和命名管道FIFO(永久的),一般用FIFO。
7、FIFO的销毁是需要手动完成的 unlink($fifo)。
8、管道的使用和文件差不多,但是他们却有着不同特性。

以下来讲解命名管道的方式。

管道用于承载进程之间的通讯数据。为了方便理解,可以将管道比作文件,进程A将数据写到管道P中,然后进程B从管道P中读取数据。php提
供的管道操作 API与操作文件的API基本一样,除了创建管道使用posix_mkfifo函数,读写等操作均与文件操作函数相同。当然,你可以直
接使用文件模拟管 道,但是那样无法使用管道的特性了。

僵尸进程
子进程结束时,父进程没有等待它(通过调用wait或者waitpid),那么子进程结束后不会释放所有资源(浪费呀!),这种进程被称为僵尸
进程,他里面存放了子进程结束时的相关数据,如果僵尸进程过多,会占用大量系统资源(如内存),影响机器性能。

管道实现:
父进程创建管道:
	$sPipePath = "/tmp/test.pipe";
子进程写入管道:file_put_contents($sPipePath, $i . PHP_EOL);
	需要注意,自己分割各个进程的内容,并判断所有子进程都写入完成

一个典型的例子:

<?php

define("PC", 10); // 进程个数
define("TO", 4); // 超时
define("TS", 4); // 事件跨度,用于模拟任务延时
 
if (!function_exists('pcntl_fork')) {
    die("pcntl_fork not existing");
}
 
// 创建管道
$sPipePath = "/tmp/test.pipe";
if( !file_exists( $sPipePath ) ){
    if( !posix_mkfifo( $sPipePath, 0666 ) ){
        exit('make pipe false!' . PHP_EOL);
    }
}
 
// 模拟任务并发
for ($i = 0; $i < PC; ++$i ) {
    $nPID = pcntl_fork(); // 创建子进程
    if ($nPID == 0) {
        // 子进程过程
        sleep(rand(1,TS)); // 模拟延时
        $result = 'data of process '.posix_getpid();
    	file_put_contents($sPipePath, $i . '-' . json_encode($result) . PHP_EOL);
    	exit();
    }
}
 
// 父进程
$oR = fopen($sPipePath, 'r');
stream_set_blocking($oR, FALSE); // 将管道设置为非堵塞,用于适应超时机制
$sData = ''; // 存放管道中的数据
$nLine = 0;
$nStart = time();
while ($nLine < PC && (time() - $nStart) < TO) {
    $sLine = fread($oR, 1024);
    if (empty($sLine)) {
        continue;   
    }   
     
    echo "current line: {$sLine}\n";
    // 用于分析多少任务处理完毕,通过‘\n’标识
    foreach(str_split($sLine) as $c) {
        if ("\n" == $c) {
            ++$nLine;
        }
    }
    $sData .= $sLine;
}
echo "Final line count:$nLine\n";
fclose($oR);
unlink($sPipePath); // 删除管道,已经没有作用了
 
// 等待子进程执行完毕,避免僵尸进程
$n = 0;
while ($n < PC) {
    $nStatus = -1;
    $nPID = pcntl_wait($nStatus, WNOHANG);
    if ($nPID > 0) {
        echo "{$nPID} exit\n";
        ++$n;
    }
}
 
// 验证结果,主要查看结果中是否每个任务都完成了
$arr2 = array();
foreach(explode("\n", $sData) as $i) {// trim all
    if (is_numeric(trim($i))) {
        array_push($arr2, $i);  
    }
}
$arr2 = array_unique($arr2);
if ( count($arr2) == PC) {  
    echo 'ok'; 
} else {
    echo  "error count " . count($arr2) . "\n";
    var_dump($arr2);
}

虽然看起来繁琐,但是能说明问题。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值