PHP 进程锁定问题分析研究
2015-01-24信息快讯网
1. 区分读锁定 和 写 锁定。
如果每次都使用 写锁定,那么连多个进程读取一个文件也要排队,这样的效率肯定不行。
2. 区分 阻塞 与 非 阻塞模式。
一般来说,如果一个进程在写一个文件的时候,另外一个进程应该被阻塞,但是,很多时候,我们可以先干点别的事情,
然后再判断一下是否有其他人在写文件,如果没有,再加入数据,这样的效率更高。
3. 修复了 锁定文件在linux 上的bug,特别是 在 gfs 文件系统上的bug。
代码如下:
<?php class File_Lock { private $name; private $handle; private $mode; function __construct($filename, $mode = 'a+b') { global $php_errormsg; $this->name = $filename; $path = dirname($this->name); if ($path == '.' || !is_dir($path)) { global $config_file_lock_path; $this->name = str_replace(array("/", "\\"), array("_", "_"), $this->name); if ($config_file_lock_path == null) { $this->name = dirname(__FILE__) . "/lock/" . $this->name; } else { $this->name = $config_file_lock_path . "/" . $this->name; } } $this->mode = $mode; $this->handle = @fopen($this->name, $mode); if ($this->handle == false) { throw new Exception($php_errormsg); } } public function close() { if ($this->handle !== null ) { @fclose($this->handle); $this->handle = null; } } public function __destruct() { $this->close(); } public function lock($lockType, $nonBlockingLock = false) { if ($nonBlockingLock) { return flock($this->handle, $lockType | LOCK_NB); } else { return flock($this->handle, $lockType); } } public function readLock() { return $this->lock(LOCK_SH); } public function writeLock($wait = 0.1) { $startTime = microtime(true); $canWrite = false; do { $canWrite = flock($this->handle, LOCK_EX); if(!$canWrite) { usleep(rand(10, 1000)); } } while ((!$canWrite) && ((microtime(true) - $startTime) < $wait)); } /** * if you want't to log the number under multi-thread system, * please open the lock, use a+ mod. then fopen the file will not * destroy the data. * * this function increment a delt value , and save to the file. * * @param int $delt * @return int */ public function increment($delt = 1) { $n = $this->get(); $n += $delt; $this->set($n); return $n; } public function get() { fseek($this->handle, 0); return (int)fgets($this->handle); } public function set($value) { ftruncate($this->handle, 0); return fwrite($this->handle, (string)$value); } public function unlock() { if ($this->handle !== null ) { return flock($this->handle, LOCK_UN); } else { return true; } } } ?>
测试代码:
<?php /** * 进行写锁定的测试 * 打开线程1 */ require("file_lock.php"); $lock = new File_Lock(dirname(dirname(__FILE__)) . "/FileLock.lock"); /** 单个线程锁定的速度 1s 钟 3万次。 **/ /** 两个线程写,两万的数据 大概要 7s 钟*/ /** 一个线程写,一万的数据 大概要 3.9s 钟,居然两个文件同时写,要快一点*/ /** 不进行锁定,一个进程 写大概要 2.8s 钟,加锁是有代价的。 */ /** 不进行锁定,两个进程 分布不是很均匀,而且大多数都冲突 */ $lock->writeLock(); $lock->increment(); $lock->unlock(); while ($lock->get() < 2) { usleep(1000); } sleep(1); echo "begin to runing \n"; $t1 = microtime(true); for ($i = 0; $i < 10000; $i++) { $lock->writeLock(); $lock->increment(1); $lock->unlock(); } $t2 = microtime(true) - $t1; echo $t2; ?>
我增加了一个 increment 的函数,可以实现简单的线程同步,让两个进程同时执行某段代码,当然,这个有一定的误差
这里的误差是 0.001s。
把这个类简单的用到 前面的memcache 消息队列中就可以实现 线程安全的消息队列。