最近在学习使用 workerman 框架,对 php 进程这块有点兴趣,于是特意研究了一下,下面将学习的一些心得总结一下,如下:
主要的还是理解 pcntl_fork pcntl_wait pcntl_waitpid 三个函数的用法,下面列举一下官方的定义。
pcntl_fork — 在当前进程当前位置产生分支(子进程)。译注:fork是创建了一个子进程,父进程和子进程 都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程 号,而子进程得到的是0。
pcntl_wait — 等待或返回 fork 的子进程状态,wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。
pcntl_waitpid — 等待或返回fork的子进程状态,挂起当前进程的执行直到参数pid指定的进程号的进程退出, 或接收到一个信号要求中断当前进程或调用一个信号处理函数。
下面是一个简单的创建子进程的示例,很清晰的说明了具体的步骤:
<?php $pid = pcntl_fork(); //父进程和子进程都会执行下面代码 if ($pid == -1) { //错误处理:创建子进程失败时返回-1. die('could not fork'); } else if ($pid) { //父进程会得到子进程号,所以这里是父进程执行的逻辑 pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。 } else { //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。 //返回当前进程id $pid = posix_getgid(); //返回当前父进程标识 $ppid = posix_getppid(); }
下面自己总结了一个利用多进程同步执行的函数,供大家参考,这里提醒一下,php多进程编程只适宜 cli 模式下,fpm 下会出问题,目前的问题是只返回最后一个进程返回的信息。
<?php /** * 多进程同步执行 * @param array $fun_list */ function multiProgress($fun_list) { for ($i = 0; $i < count($fun_list); $i++) { $pid = pcntl_fork(); switch ($pid) { case -1: myLog('子进程创建失败'); break; case 0: $fun_list[$i](); exit(0); break; default: $childPids[] = $pid; break; } } while (count($childPids) > 0) { foreach ($childPids as $key => $pid) { $res = pcntl_waitpid($pid, $status, WNOHANG); if ($res == -1 || $res > 0) { unset($childPids[$key]); } } if (count($childPids) == 0) { break; } } } function myLog($str) { echo date('Y-m-d H:i:s') . ' ' . $str . "\r\n"; }
具体调用示例:
multiProgress([function () { myLog('开始执行第1个进程'); sleep(3); myLog('第1个进程执行完毕退出'); }, function () { myLog('开始执行第2个进程'); sleep(1); myLog('第2个进程执行完毕退出'); }]); myLog('执行完毕');
执行结果如下图:
也可以像下面这样使用:
$url_list = [ 'https://www.workerman.net/', 'https://www.taobao.com', 'https://www.laruence.com/', '//' ]; $file1 = './x1.txt'; file_exists($file1) && unlink($file1); $time_start = microtime(true); foreach ($url_list as $key => $item) { $a[] = function () use ($key, $item) { $content = file_get_contents($item); file_put_contents($GLOBALS['file2'], ($key + 1) . "\r\n" . str_replace(["\r\n", ' ', "\r", "\n"], '', $content) . "\r\n\r\n\r\n", FILE_APPEND ); }; } multiProgress($a);