我们可以利用PHP的PCNTL模块,写多进程脚本,我整理了一个模板,代码如下:

<?php

if (PHP_SAPI != 'cli') {
    exit('请在命令行模式下执行');
}


//脚本永不超时
set_time_limit(0);


/**
 * 多进程类封装
 */
class MultiProgress {

    /**
     * 允许同时运行的子进程数量
     * @var int
     */
    private $maxChildNum;

    /**
     * 子进程pid列表
     * @var array
     */
    private $childList = array();

    /**
     * 构造函数
     * @param int $maxChildNum 允许同时运行的子进程数量
     */
    public function __construct($maxChildNum = 5) {
        $this->maxChildNum = $maxChildNum;
    }

    /**
     * 执行
     */
    public function run() {
        //让内核自动回收子进程,防止产生僵尸进程
        pcntl_signal(SIGCHLD, SIG_IGN);
        //制造一个死循环,让脚本一直跑
        while (true) {
            $result = $this->doSomething();
            //如没有业务被处理,则休眠10秒再继续
            if (!$result) {
                sleep(10);
            }
        }
    }

    /**
     * 业务处理逻辑
     * @return boolean 是否有业务被处理了
     */
    private function doSomething() {
        //查询消息待推送列表
        $rows = $this->getMsgList();
        //标记是否有数据被处理
        $flag = $rows ? true : false;

        foreach ($rows as $row) {
            //在当前进程当前位置产生分支(子进程)
            $pid = pcntl_fork();

            if ($pid == -1) { //错误处理:创建子进程失败时返回-1
                exit("创建子进程失败");
            } elseif ($pid) { //父进程会得到子进程的pid号,所以这里是父进程执行的逻辑
                $this->childList[$pid] = $pid;
                while ($this->checkChildStatus()) {
                    sleep(1); //如果子进程已满,则休眠1秒再检测
                }
            } else { //子进程得到的pid为0,所以这里是子进程执行的逻辑
                //得到当前子进程的pid
                $mypid = getmypid();

                $time = date('Y-m-d H:i:s');
                $row = trim($row);
                $log = "------------------------\n";
                $log .= "$mypid $time\n";
                $log .= "$mypid 推送消息:$row\n";
                $log .= "$mypid 推送完毕\n";
                echo $log;

                //必须kill掉子进程,不然会成为僵尸进程
                posix_kill($mypid, SIGKILL);
            }
        }

        return $flag;
    }

    /**
     * 检测子进程状态,判断是否已达到数量上限
     * @return boolean
     */
    private function checkChildStatus() {
        foreach ($this->childList as $pid) {
            $res = pcntl_waitpid($pid, $status, WNOHANG);
            if ($res == -1 || $res > 0) { //子进程已结束
                unset($this->childList[$pid]);
            }
        }

        if (count($this->childList) == $this->maxChildNum) {
            $isMax = true;
        } else {
            $isMax = false;
        }

        return $isMax;
    }

    /**
     * 这里只是模拟获取待推送的数据。
     * 请根据你的实际需求更改,如查mysql、redis。
     * @return array
     */
    private function getMsgList() {
        $dataFile = __DIR__ . '/a.txt';
        if (is_file($dataFile)) {
            $arr = file($dataFile); //读取文件的每一行,存入数组
            unlink($dataFile); //读完就删除,避免重复读
            return $arr;
        } else {
            return [];
        }
    }

}


//示例
$obj = new MultiProgress(10);
$obj->run();

?>

当前,这个模块不支持Windows系统,需要Linux系统才支持(即非Unix类系统不支持此模块)。还有就是,在编译PHP的时候要加上 --enable-pcntl 选项它才会被编译。

在Webserver环境下(如Apache、Nginx等),运行此模块会有不可预料的结果,因此,你只能在命令行模式下执行。

本文参考:http://www.laruence.com/2009/06/11/930.html