+ -
当前位置:首页 → 问答吧 → PHP flock函数与系统消息队列的关系!?

PHP flock函数与系统消息队列的关系!?

时间:2013-05-10

来源:互联网

本帖最后由 除美灭日平韩 于 2013-5-10 09:51 编辑

本人在开发过程中用到了文件锁flock函数
然后又加了个C扩展调用一个C扩展与一个C服务端连接
(比如向系统消息队列发消息,另外一个客户端会去取这些消息)
该C服务端会使用系统的消息队列
在测试过程中会出现这样一种情况
当取消息的客服端停掉后不再取消息
等消息队列的消息数满后
PHP的flock函数就会出现阻塞,无法锁定,导致程序无法执行

这个问题不知道是不是与消息队列有关系,为什么有关系
本人水平有限,不知道怎么查,不知哪位高手遇到此种情况


作者: 除美灭日平韩   发布时间: 2013-05-10

贴代码上来, 通常是流程问题.

作者: spser   发布时间: 2013-05-10

在没有使用C扩展的时候都是正常的,现在已经确认是那个消息服务器的问题
我把代码放出来吧,里面有个内存锁但是WEB模式与CLI模式不能共享内存
导致该锁无效,也帮忙看看

作者: 除美灭日平韩   发布时间: 2013-05-10

本帖最后由 除美灭日平韩 于 2013-5-10 10:51 编辑

<?php 
class Cachelock
{
        /**
         * $lock_timeout 设置等待回旋次数
         * $lock_wait_func 设置等待机制,本例使用usleep+mt_rand随机等待,随机等待有利错开多个竞争
         * $add_func 这里设置添加锁标志机制,本例使用PHP的APC组件apc_add函数,apc_add设定锁占有上限为5s,避免永久占有
         * $del_func 这里设置删除锁标志机制,本例使用PHP的APC组件apc_delete函数
         */
        private $locks = array();
        private $lock_timeout = 200;
        private $lock_wait_func;
        private $add_func;
        private $del_func;
        private $mutex =0;
        private $shmid = null;
        
        //文件锁存放路径
        private $path = null;
        //文件句柄
        private $fp = null;
        //锁粒度,设置越大粒度越小
        private $hashNum = 100;
        //cache key
        private $name;
        /**
         * 
         * 内存锁与文件锁的开关
         * @var bool
         */
        public  $is_memory=FALSE;
        
        
        public function __construct()
        {
//                $this->add_func = function($mutex)
//                {
//                        return apc_add('sl:'.$mutex,1,5);
//                };
//
//                $this->del_func = function($mutex)
//                {
//                        return apc_delete('sl:'.$mutex);
//                };
//
//                $this->lock_wait_func = function()
//                {
//                        usleep(mt_rand(10000,50000));
//                };
        }

        public function __destruct()
        {
                $this->clean();
        }

        
          public function add_func(){
                  if(shm_has_var($this->shmid,$this->mutex)){
                          return FALSE;
                  }
                  return shm_put_var($this->shmid,$this->mutex,1);

          }
        
          public function del_func()
                {
                          return shm_remove_var($this->shmid,$this->mutex);  
                }

                
        /**
         * 
         * 创建共享内存
         */
        public function cls_shm($mutex=''){
                if($mutex){
                        $this->mutex = $this->_mycrc32($mutex);
                }
                if(!$this->shmid && $this->mutex){
                         $this->shmid = shm_attach( $this->mutex, $this->memsize, $this->perm ); // 创建一个共享内存
                }
                
        }
        /**
         * 清除当前所有设置的锁,在当前的php进程中可以设置多个锁
         */
        public function clean()
        {
                if($this->is_memory){
                        if($this->locks)
                        {
                                foreach($this->locks as $lock => $tmp)
                                $this->del_func($lock);
                                $this->locks = array();
                        }
                        shm_remove($this->shmid);
                }
        }



        /**
         * 新建立一个锁
         * 首先会判断锁定标志是否已经定义,如果已锁定则判定为死锁
         * 其次使用apc共享内存方式add一个锁标志,如果失败则进入等待时间,直到超时
         */
        public function lock($mutex,$path='')
        {
                if($this->is_memory){
                        //初始化
                        $this->cls_shm($mutex);
                        if(isset($this->locks[$this->mutex]) && $this->locks[$this->mutex]!=null)
                        {
                                return false;
                        }

                        while($this->add_func() == false)
                        {
                                usleep(mt_rand(10000,50000));
                        }
                        $this->locks[$this->mutex] = 1;
                        return $this->mutex;
                }else{
                        //$this->path = $path.($this->_mycrc32($mutex) % $this->hashNum).'.txt';
                        $this->path = $path.$mutex.'.txt';
                        $this->name = $mutex;
                        //配置目录权限可写
                        $this->fp = fopen($this->path,'a');
                        if($this->fp === false)
                        {
                                return false;
                        }
                        return flock($this->fp, LOCK_EX|LOCK_NB);
                }
        }


        /**
         * 手动释放锁,一般不用,
         * 在当前对象析构时会自动释放所有锁
         */
        public function release($mutex)
        {
                if($mutex == false) return false;
                if($this->is_memory){
                        unset($this->locks[$this->mutex]);
                        $this->del_func();
                        return true;
                }else{
                        if($this->fp !== false)
                        {
                                flock($this->fp, LOCK_UN);
                                clearstatcache();
                        }
                        //进行关闭
                        fclose($this->fp);
                }
        }
        
        
        /**
         * crc32
         * crc32封装
         * @param int $string
         * [url=home.php?mod=space&uid=987628]@Return[/url] int
         */
        private function _mycrc32($string)
        {
                $crc = abs (crc32($string));
                if ($crc & 0x80000000) {
                        $crc ^= 0xffffffff;
                        $crc += 1;
                }
                return $crc;
        }
        
        
}
?>

作者: 除美灭日平韩   发布时间: 2013-05-10

调用很简单就是
if ($this->cachelock->lock('lock')){
        ......................
        $this->cachelock->release('lock');
}

作者: 除美灭日平韩   发布时间: 2013-05-10

本帖最后由 spser 于 2013-5-10 11:04 编辑

return flock($this->fp, LOCK_EX|LOCK_NB);
加个日志追踪.  它锁的同时可能也有并发锁, 需要监控. 写入建议不要用LOCK_NB, 让它堵塞吧,

作者: spser   发布时间: 2013-05-10

实际是在flock($this->fp, LOCK_EX|LOCK_NB);这个地方阻塞了,流程无法走下去了

作者: 除美灭日平韩   发布时间: 2013-05-10

加了那个LOCK_NB也没有立即返回,而是卡住了

作者: 除美灭日平韩   发布时间: 2013-05-10

本帖最后由 spser 于 2013-5-10 11:23 编辑

那的确是flock被并发了.
php版本是多少? 升到新版试试.
linux不应该发生这种事才对的.

作者: spser   发布时间: 2013-05-10

flock($this->fp, LOCK_EX);
这样看看会不会卡住.

作者: spser   发布时间: 2013-05-10

spser 发表于 2013-5-10 11:23
flock($this->fp, LOCK_EX);
这样看看会不会卡住.

会的,加不加LOCK_NB都会卡住

作者: 除美灭日平韩   发布时间: 2013-05-10