PHP多进程编程

时间:2022-02-23 16:41:23

PHP本身不支持多线程,多进程支持的也不是特别好,网上找到一份多进程代码(http://jasonyu.cn/html/2010/294.html),写了一个测试程序简单测了一下,可以运行,但是离上线还差得很远。

<?php
/**
 * Project: Signfork: php多线程库
 * File: Signfork.class.php
 */

class Signfork{
    /**
     * 设置子进程通信文件所在目录
     * @var string
     */
    private $tmp_path='/tmp/';

    /**
     * Signfork引擎主启动方法
     * 1、判断$arg类型,类型为数组时将值传递给每个子进程;类型为数值型时,代表要创建的进程数.
     * @param object $obj 执行对象
     * @param string|array $arg 用于对象中的__fork方法所执行的参数
     * 如:$arg,自动分解为:$obj->__fork($arg[0])、$obj->__fork($arg[1])…
     * @return array 返回 array(子进程序列=>子进程执行结果);
     */
    public function run($obj,$arg=1){
        if(!method_exists($obj,'__fork')){
            exit('Method "__fork" not found!');
        }

        if(is_array($arg)){
            $i=0;
            foreach($arg as $key=>$val){
                $spawns[$i]=$key;
                $i++;
                $this->spawn($obj,$key,$val);
            }
            $spawns['total']=$i;
        }elseif($spawns=intval($arg)){
            for($i = 0; $i < $spawns; $i++){
                $this->spawn($obj,$i);
            }
        }else{
            exit('Bad argument!');
        }

        if($i>1000) exit('Too many spawns!');
        return $this->request($spawns);
    }

    /**
     * Signfork主进程控制方法
     * 1、$tmpfile 判断子进程文件是否存在,存在则子进程执行完毕,并读取内容
     * 2、$data收集子进程运行结果及数据,并用于最终返回
     * 3、删除子进程文件
     * 4、轮询一次0.03秒,直到所有子进程执行完毕,清理子进程资源
     * @param string|array $arg 用于对应每个子进程的ID
     * @return array 返回 array([子进程序列]=>[子进程执行结果]);
     */
    private function request($spawns){
        $data=array();
        $i=is_array($spawns)?$spawns['total']:$spawns;
        for($ids = 0; $ids<$i; $ids++){
            while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000);
            $tmpfile=$this->tmp_path.'sfpid_'.$cid;
            $data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile);
            unlink($tmpfile);
        }
        return $data;
    }

    /**
     * Signfork子进程执行方法
     * 1、pcntl_fork 生成子进程
     * 2、file_put_contents 将'$obj->__fork($val)'的执行结果存入特定序列命名的文本
     * 3、posix_kill杀死当前进程
     * @param object $obj 待执行的对象
     * @param object $i 子进程的序列ID,以便于返回对应每个子进程数据
     * @param object $param 用于输入对象$obj方法'__fork'执行参数
     */
    private function spawn($obj,$i,$param=null){
        if(pcntl_fork()===0){
            $cid=getmypid();
            file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param));
            posix_kill($cid, SIGTERM);
            exit;
        }
    }
}
?>

测试程序如下:

<?php
require_once(dirname(__FILE__) . '/Signfork.class.php');
class ExecObj{
    public function __fork($param){
        return getmypid();
    }
    
}
echo getmypid()."\n";
$signfork = new Signfork();
$result = $signfork->run(new ExecObj(),5);
foreach ($result as $key=>$val)
{
    echo $key." ".$val."\n";
}
?>