PHP CLI模式下的多进程应用可以通过PHP的pcntl和posix扩展来实现。本攻略将介绍如何使用这两个扩展来实现多进程的应用。
安装pcntl和posix扩展
PHP CLI模式默认不包含pcntl和posix扩展,需要手动安装。下面是安装命令的参考样例:
Debian / Ubuntu
sudo apt-get install php-pcntl sudo apt-get install php-posix
Fedora / CentOS
sudo yum install php-pcntl sudo yum install php-posix
实现多进程应用
创建子进程
使用pcntl_fork函数可以创建一个子进程,示例代码如下:
$pid = pcntl_fork(); if ($pid === -1) { exit("fork() failed\n"); } else if ($pid) { // 父进程代码 } else { // 子进程代码 }
这段代码中,pcntl_fork函数会返回两次,第一次在父进程中返回子进程的PID,第二次在子进程中返回0。如果返回值为-1,则说明fork()函数执行失败。
父子进程的共享与分离
父子进程间共享的变量可以通过共享内存或消息队列来实现。具体可以使用PHP的shmop和msg扩展来实现。
分离进程是指子进程与父进程脱离关系,执行独立的代码。一般情况下,应该在子进程中执行分离操作。示例代码如下:
posix_setsid();
注意:在分离进程之前,必须先关闭打开的文件、socket等资源,否则会导致资源泄漏。
进程间通信
使用共享内存或消息队列可以实现进程间通信。
示例
多进程排序
以下示例展示了如何使用多进程实现数组排序。代码如下:
<?php $num = $argv[1] ?? 1000; $array = range(1, $num); shuffle($array); $pid = pcntl_fork(); if ($pid === -1) { exit("fork() failed\n"); } else if ($pid) { // 父进程 $start = microtime(true); pcntl_waitpid($pid, $status); $end = microtime(true); $result = unserialize(shmop_read($shmid, 0, $segment_size)); echo "Sorted array: " . implode(", ", $result) . "\n"; echo "Parent process time used: " . ($end - $start) . " seconds\n"; } else { // 子进程 $start = microtime(true); $shmid = shmop_open(ftok(__FILE__, 't'), "c", 0666, $num * 4); $segment_size = shmop_size($shmid); $array = array_chunk($array, ceil(count($array) / 2)); foreach ($array as $sub_array) { sort($sub_array); $data = serialize($sub_array); shmop_write($shmid, str_pad($data, $segment_size, "\0"), 0); } exit(0); // 注意:必须在子进程中使用exit语句,不然会继续执行父进程的代码 }
多进程下载
以下示例展示了如何使用多进程实现多文件并行下载。代码如下:
<?php $urls = ['http://www.phpernote.com/file1', 'http://www.phpernote.com/file2', 'http://www.phpernote.com/file3']; $workers = count($urls); for ($i = 0; $i < $workers; $i++) { $pid = pcntl_fork(); if ($pid === -1) { exit("fork() failed\n"); } else if ($pid) { // 父进程 pcntl_waitpid($pid, $status); } else { // 子进程 $url = $urls[$i]; $filename = basename($url); $fp = fopen($filename, 'w'); $ch = curl_init($url); curl_setopt($ch, CURLOPT_FILE, $fp); curl_exec($ch); fclose($fp); exit(0); } }