PHP 多进程 解决难题
背景
随着互联网的快速发展,现代网站往往承载着大量的请求和用户访问。这些访问一般都需要进行计算和处理,处理数据的计算量逐渐变得越来越大,单线程的方式难以满足这些需求,需要使用多进程的技术来提高并发性能。
多进程实现方式
在PHP中,多进程可以通过开多线程或者创建子进程来实现。其中,开多线程是常见做法,但是多线程带来的内存分配、数据共享、线程同步等问题需要特殊关注。所以相对来说,PHP创建子进程的方案更为常用和实践。
在PHP中创建子进程的方法有很多,比如pcntl_fork(), pcntl_exec(), proc_open()等,这里只介绍较为常用的pcntl_fork()。
pcntl_fork()的用途是开启一个子进程,当前进程(parent process
)会克隆一个子进程(child process
)并分别在父进程和子进程中执行不同的任务。可以利用子进程来分压在主进程中的一些繁重任务,达到协作的目的。
示例一:多进程爬虫
多进程爬虫适用于大量URL访问,并且URL访问数目不可预测的场景。比如,爬取多个网页、API、爬虫数据等。
下面是一个示例程序:
<?php
$urlList = [
'https://www.baidu.com',
'http://www.sina.com',
'https://www.qq.com',
'https://www.360.cn',
'http://wenda.so.com'
];
$maxProcesses = 3; // 最大进程数
// 创建子进程
echo "Parent PID: " . getmypid() . PHP_EOL; // 输出主进程的PID
while (count($urlList) > 0) {
while (count($urlList) > 0 && $activeProcesses < $maxProcesses) {
$pid = pcntl_fork();
if ($pid == -1) {
exit("Error: fork failed\n");
} elseif ($pid) {
$activeProcesses++; // 父进程
$childs[] = $pid;
array_shift($urlList); // 从待处理URL队列中删除一个URL
} else {
$url = array_shift($urlList); // 从待处理URL队列中取出一个URL
// do some stuff
sleep(rand(0, 9)); // 模拟请求等待时间
echo "Child PID: " . getmypid() . ", URL: " . $url . PHP_EOL;
exit(); // 子进程退出,避免继续执行父进程的代码
}
}
// 等待子进程结束并更新跟踪列表
if ($activeProcesses > 0) {
foreach ($childs as $key => $pid) {
$res = pcntl_waitpid($pid, $status, WNOHANG);
// 如果子进程退出,将它从childs数组中删除
if ($res == -1 || $res > 0) {
$activeProcesses--;
unset($childs[$key]);
}
}
}
}
// 等待所有子进程结束
while (count($childs) > 0) {
foreach ($childs as $key => $pid) {
$res = pcntl_waitpid($pid, $status, WNOHANG);
// 如果子进程退出,将它从childs数组中删除
if ($res == -1 || $res > 0) {
unset($childs[$key]);
}
}
}
echo "Done.\n";
上面的示例代码通过创建子进程来并发爬取URL,可以看到执行结果中父进程的PID和每个子进程在执行的时候都会输出它的PID和处理的URL。
示例二:多进程大文件处理
多进程大文件处理适用于对大型文件进行分片处理的场景,比如分析日志文件、统计大量数据等。使用多进程可以快速处理文件,提高处理速度。
下面是一个示例程序:
<?php
// 要处理的大文件
$fileName = 'bigdata.log';
// 分片数量
$sliceNumber = 10;
// 创建子进程
echo "Parent PID: " . getmypid() . PHP_EOL;
for ($i = 0; $i < $sliceNumber; $i++) {
$pid = pcntl_fork();
if ($pid == -1) {
exit("Error: fork failed\n");
} elseif ($pid) {
$childs[] = $pid;
} else {
// 打开文件
$fh = fopen($fileName, 'r');
// 定位到起始位置
fseek($fh, ($i * filesize($fileName)) / $sliceNumber);
// 读取文件中的一部分数据
$data = fread($fh, filesize($fileName) / $sliceNumber);
// 对数据进行处理操作
// ...
echo "Child PID: " . getmypid() . ", Slice No: " . $i . PHP_EOL;
// 关闭文件句柄
fclose($fh);
// 子进程退出
exit();
}
}
// 等待所有子进程结束
while (count($childs) > 0) {
foreach ($childs as $key => $pid) {
$res = pcntl_waitpid($pid, $status, WNOHANG);
// 如果子进程退出,将它从childs数组中删除
if ($res == -1 || $res > 0) {
unset($childs[$key]);
}
}
}
echo "Done.\n";
上面的示例代码通过对大型文件进行分片处理,可以看到执行结果中父进程的PID和每个子进程在执行的时候都会输出它的PID和处理的片段序号。
结论
多进程是一种提高PHP应用并发性能的有效方式,可以利用多核CPU资源进行并发处理,适用于大量请求和高计算量的场景。在使用多进程的过程中,需要注意进程同步和资源共享等问题。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:PHP 多进程 解决难题 - Python技术站