PHP并发操作下的加锁
php如何解决多线程读写同一文件
大家都知道,PHP是没有多线程概念的,尽管如此我们仍然可以用“不完美”的方法来模拟多线程。简单的说,就是队列处理。通过对文件进行加锁和解锁,来实现。当一个文件被一个用户操作时,该文件是被锁定的,其他用户只能等待,确实不够完美,但是也可以满足一些要求不高的应用。
有这么一个需求:生成文件的时候,由于多用户都有权限进行生成,防止并发下,导致生成的结果出现错误,需要对生成的过程进行加锁,只容许一个用户在一个时间内进行操作,这个时候就需要用到锁了,将这个操作过程锁起来。在用了cache的时候,cache失效可能导致瞬间的多数并发请求穿透到数据库此时也可以得需要用锁在同一并发的过程中将这个操作锁定。
针对以上的2种情况,现在的解决方法是对处理过程进行锁机制,通过PHP实现如下:
用到了Eaccelerator的内存锁和文件锁,原理:判断系统中是否安了EAccelerator如果有则使用内存锁,如果不存在,则进行文件锁。根据带入的key的不同可以实现多个锁直接的并行处理,类似Innodb的行级锁。
具体类如下:
<?php/** * CacheLock 进程锁,主要用来进行cache失效时的单进程cache获取,防止过多的SQL请求穿透到数据库 * 用于解决PHP在并发时候的锁控制,通过文件/eaccelerator进行进程间锁定 * 如果没有使用eaccelerator则进行进行文件锁处理,会做对应目录下产生对应粒度的锁 * 使用了eaccelerator则在内存中处理,性能相对较高 * 不同的锁之间并行执行,类似mysql innodb的行级锁 * 本类在sunli的phplock的基础上做了少许修改 http://code.google.com/p/phplock * @author yangxinqi * */class CacheLock{//文件锁存放路径private $path = null;//文件句柄private $fp = null;//锁粒度,设置越大粒度越小private $hashNum = 100;//cache keyprivate $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);}$this->path = $path.($this->_mycrc32($name) % $this->hashNum).'.txt';}$this->name = $name;}/** * 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);}}}?>
?使用如下:
$lock = new CacheLock('key_name');$lock->lock();//logic here$lock->unlock();//使用过程中需要注意下文件锁所在路径需要有写权限.