有时候在PHP程序中同时只允许一个进程在操作或者只想让一个进程去执行,这时就需要加锁,在网络上找到了phplock这个项目。phplock用于多进程模式下PHP并发操作加锁,以防止并发对同一文件的读写操作错误和缓存失效时,大量请求直接穿透到数据库,造成数据库压力宕机。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | <?php /** * PHPLock进程锁 * 本进程锁用来解决php在并发时候的锁控制 * 他根据文件锁来模拟多个进程之间的锁定,效率不是非常高。如果文件建立在内存中,可以大大提高效率。 * PHPLOCK在使用过程中,会在指定的目录产生$hashNum个文件用来产生对应粒度的锁。不同锁之间可以并行执行。 * 这有点类似mysql的innodb的行级锁,不同行的更新可以并发的执行。 * @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); } } } |
测试的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?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