继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

PHP PCNTL多进程编程

牧羊人nacy
关注TA
已关注
手记 52
粉丝 5
获赞 25

PHP中的PCNTL可以实现多进程编程,由于项目场景需要,试用了一下,感触颇多,也长了不少见识,就此对遇到的问题小做一个总结,以备不时之需。


问题一:fork泛滥

我想在一个父进程中起10个子程来完成我的工作,代码如下:

for($i = 0; $i < 10; $i++) {
    $pid = pcntl_fork();
    if($pid  == -1) {
        echo "Could not fork!\n";
        exit(1);
    }
    if(!$pid) {
        //child process workspace
        //TODO
        Api::refreshCache();       
    }
}

由于Api::refreshCache逻辑中有数据库操作,代码一执行,就把DB搞死了,报了N多个too many connections。我以为是DB原本就抽风了,检查DB,正常。

最终发现,这一段代码fork的可不是10个子进程,而是

20 + 21 + 22+ 23 + 24 + ... + 29 = 210-1 = 1023 个子进程。

恐怖了吧?这是因为子进程又fork子进程,并且子进程不共享父进程$i变量更新的值,由此导致数量成指数关系增长。

为了避免这个问题,代码修改如下:

for($i = 0; $i < 10; $i++) {
    $pid = pcntl_fork();
    if($pid  == -1) {
        echo "Could not fork!\n";
        exit(1);
    }
    if(!$pid) {
        //child process workspace
        //TODO
        Api::refreshCache();
        exit; //子进程逻辑执行完后,马上退出,以免往下走再fork子进程,不好控制    
    }
}

只要在子进程逻辑执行完后,加一个exit,一切都在撑握中了。这样,只有一个父进程,父进程后续对子进程的管理也会清晰很多。


问题二:单例模式下DB连接被虐

/*
 * 前面或远或近的地方,已经有一个$db = &MySql::getInstance();了
 */
for($i = 0; $i < 10; $i++) {
    $pid = pcntl_fork();
    if($pid  == -1) {
        echo "Could not fork!\n";
        exit(1);
    }
    if(!$pid) {
        //child process workspace
        //TODO
        $db = &MySql::getInstance();
        $sql = "XXX";
        $result = $db->getAll($sql);
        exit;
    }
}


这段代码,十有八九都会报mysql has gone away.或者其它一些数据fetch方面的错误。究其原因,是因为各个子进程创建时,就已经继承了父进程一份完全一样的拷贝。对象可以拷贝,但是已创建的连接可不能拷贝成多个,由此产生的结果,就是各个进程都使用同一个mysql连接,各干各的事,最终产生莫名其妙的冲突。

解决办法:

我们显然不可能完全保证在fork进程之前,父进程不会创建mysql连接实例,因此,解决方案只能靠子进程本身了。可以想象,我们只需要在子进程中获取的实例只与当前进程相关,这个问题就不存在了。解决办法就是稍微改造一下mysql类实例化的静态方式,与当前进程ID绑定。

public static function &getInstance() {
    static $instances = array();
    $key = getmypid(); //获取当前进程ID
    if(empty($instances[$key])) {
        //实例化mysql类动作
        $classname = __CLASS__;
        $instances[$key] = new $classname();
    }
    return $instances[$key];
}

总结:子进程创建时,拷贝了父进程当时拥有的所有资源,虽说后面对资源的修改互不影响,但DB连接却还是同一个,造成运行混乱。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP