天天看点

PHP并发 加悲观锁(1)

php如何解决多线程读写同一文件

大家都知道,php是没有多线程概念的,尽管如此我们仍然可以用“不完美”的方法来模拟多线程。简单的说,就是队列处理。通过对文件进行加锁和解锁,来实现。当一个文件被一个用户操作时,该文件是被锁定的,其他用户只能等待,确实不够完美,但是也可以满足一些要求不高的应用。

上限判断,关键数据的写入扣钱之类

用到了eaccelerator的内存锁和文件锁,原理:判断系统中是否安了eaccelerator如果有则使用内存锁,如果不存在,则进行文件锁。根据带入的key的不同可以实现多个锁直接的并行处理 ,类似innodb的行级锁。

具体类如下:file_put_contents($file, $string, lock_ex );

PHP并发 加悲观锁(1)

<?php  

/** 

 * cachelock 进程锁,主要用来进行cache失效时的单进程cache获取,防止过多的sql请求穿透到数据库 

 * 用于解决php在并发时候的锁控制,通过文件/eaccelerator进行进程间锁定 

 * 如果没有使用eaccelerator则进行进行文件锁处理,会做对应目录下产生对应粒度的锁 

 * 使用了eaccelerator则在内存中处理,性能相对较高 

 * 不同的锁之间并行执行,类似mysql innodb的行级锁 

 * 

 */  

class cachelock  

{  

    //文件锁存放路径  

    private $path = null;  

    //文件句柄  

    private $fp = null;  

    //锁粒度,设置越大粒度越小  

    private $hashnum = 100;  

    //cache key  

    private $name;  

    //是否存在eaccelerator标志  

    private $eaccelerator = false;  

    /** 

     * 构造函数 

     * 传入锁的存放路径,及cache key的名称,这样可以进行并发 

     * @param string $path 锁的存放目录 

     * @param string $name cache key 

     */  

    public function __construct($name, $path = 'lock')  

    {  

        //判断是否存在eaccelerator,这里启用了eaccelerator之后可以进行内存锁提高效率  

        $this->eaccelerator = function_exists("eaccelerator_lock");  

        if (!$this->eaccelerator) {  

            if (!is_dir($path)) { //目录设置写权限  

                mkdir($path, 0777);  

                chmod($path, 0777);  

            }  

            $this->path = realpath($path) . directory_separator . ($this->_mycrc32($name) % $this->hashnum) . '.txt';  

        }  

        $this->name = $name;  

    }  

     * crc32 

     * crc32封装 相同的string会算出唯一值 

     * @param string $string 

     * @return int 

    private function _mycrc32($string)  

        $crc = abs(crc32($string));  

        if ($crc & 0x80000000) {  

            $crc ^= 0xffffffff;  

            $crc += 1;  

        return $crc;  

     * 加锁 

     * enter description here ... 

    public function lock()  

        //如果无法开启ea内存锁,则开启文件锁  

            //配置目录权限可写  

            $this->fp = fopen($this->path, 'w+');  

            if ($this->fp === false) {  

                return false;  

            return flock($this->fp, lock_ex);  

        } else {  

            return eaccelerator_lock($this->name);  

     * 解锁 

    public function unlock()  

            if ($this->fp !== false) {  

                flock($this->fp, lock_un);  

                $this->deletecache();  

            //进行关闭  

            fclose($this->fp);  

            return eaccelerator_unlock($this->name);  

    //删除零时文件  

    private function deletecache()  

        clearstatcache(); //清除文件状态缓存  

        //删除1小时前的文件  

        $cachedir = dirname($this->path);  

        $files = scandir($cachedir);  

        foreach ($files as $file) {  

            if (strlen($file) > 2 && time() > (filemtime($cachedir . directory_separator . $file) + 3600)) {  

                @unlink($cachedir . directory_separator . $file);  

}  

?>  

 使用如下:

PHP并发 加悲观锁(1)

$lock = new cachelock($orderno); //要排队访问文件要相同,即name  

$lock->lock();  

//logic here  

$lock->unlock();