PHP并发操作加锁

有时候在PHP程序中同时只允许一个进程在操作或者只想让一个进程去执行,这时就需要加锁,在网络上找到了phplock这个项目。phplock用于多进程模式下PHP并发操作加锁,以防止并发对同一文件的读写操作错误和缓存失效时,大量请求直接穿透到数据库,造成数据库压力宕机。

<?php
/**
 * PHPLock进程锁
 * 本进程锁用来解决php在并发时候的锁控制
 * 他根据文件锁来模拟多个进程之间的锁定,效率不是非常高。如果文件建立在内存中,可以大大提高效率。
 * PHPLOCK在使用过程中,会在指定的目录产生$hashNum个文件用来产生对应粒度的锁。不同锁之间可以并行执行。
 * 这有点类似mysql的innodb的行级锁,不同行的更新可以并发的执行。
 * @link http://code.google.com/p/phplock/
 * @author sunli
 * @blog http://sunli.cnblogs.com
 * @svnversion  $Id$
 * @version v1.0 beta1
 * @license Apache License Version 2.0
 * @copyright  sunli1223@gmail.com
 */
class PHPLock
{
    //文件锁存放路径
    private $path = null;
    //文件句柄
    private $fp = null;
    //锁粒度,设置越大粒度越小
    private $hashNum = 100;
    //cache key
    private $name=null;
    //是否存在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)
        {
            $this->path = $path.($this->_mycrc32($name) % $this->hashNum).'.lock';
        }
        $this->name = $name;
    }
    public function __destruct()
    {
        $this->unlock();
    }

    /**
     * crc32
     * crc32封装
     * @param int $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内存锁,则开启文件锁
        if(!$this->eAccelerator)
        {
            //配置目录权限可写
            $this->fp = fopen($this->path, 'w+');
            if($this->fp === false)
            {
                return false;
            }
            return flock($this->fp, LOCK_EX);
        }else{
            return eaccelerator_lock($this->name);
        }
    }

    /**
     * 解锁
     * Enter description here ...
     */
    public function unlock()
    {
        if(!$this->eAccelerator)
        {
            if($this->fp !== false)
            {
                flock($this->fp, LOCK_UN);
                clearstatcache();

                fclose($this->fp);
            }
        }else{
            return eaccelerator_unlock($this->name);
        }
    }
}

测试的代码

<?php
require 'class.phplock.php';
$lock = new PHPLock('key_name');
if($lock->lock()){
    //do something here
    //进程安全的操作
    $lock->unlock();
}
else{
    //do something here
}

//使用过程中需要注意下文件锁所在路径需要有写权限.

需要注意的是在部分操作系统中,flock() 以处理级执行。当用一个多线程服务器 API(比如 ISAPI)时,可能不可以依靠 flock() 来保护文件,因为在同一服务器内运行在其它线程的 PHP 脚本可以对该文件进行处理。
参考链接:
并发下常见的加锁及锁的PHP具体实现
phplock
best way to obtain a lock in php

发表评论

电子邮件地址不会被公开。 必填项已用*标注