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);
}
虽然看起来繁琐,但是能说明问题。
php多进程-管道
最新推荐文章于 2024-01-25 22:05:37 发布