PHP 进程锁定问题分析研究


Posted in PHP onNovember 24, 2009

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 消息队列中就可以实现 线程安全的消息队列。
PHP 相关文章推荐
[转帖]PHP世纪万年历
Dec 06 PHP
PHP IN_ARRAY 函数使用注意事项
Jul 24 PHP
php中flush()、ob_flush()、ob_end_flush()的区别介绍
Feb 17 PHP
如何使用PHP获取指定日期所在月的开始日期与结束日期
Aug 01 PHP
PHP按行读取文件时删除换行符的3种方法
May 04 PHP
Yii结合CKEditor实现图片上传功能
Jun 13 PHP
PHP中抽象类、接口的区别与选择分析
Mar 29 PHP
php实现登陆模块功能示例
Oct 20 PHP
php封装json通信接口详解及实例
Mar 07 PHP
php调用云片网接口发送短信的实现方法
Oct 25 PHP
PHP中“=&gt;
Mar 01 PHP
laravel 数据迁移与 Eloquent ORM的实现方法
Apr 12 PHP
PHP 递归效率分析
Nov 24 #PHP
PHP 单引号与双引号的区别
Nov 24 #PHP
PHP小程序自动提交到自助友情连接
Nov 24 #PHP
php 引用(&amp;)详解
Nov 20 #PHP
php+javascript的日历控件
Nov 19 #PHP
php与XML、XSLT、Mysql的结合运用实现代码
Nov 19 #PHP
php 静态变量的初始化
Nov 15 #PHP
You might like
apache mysql php 源码编译使用方法
2012/05/03 PHP
php中选择什么接口(mysql、mysqli)访问mysql
2013/02/06 PHP
php session的锁和并发
2016/01/22 PHP
php实现图片上传时添加文字和图片水印技巧
2020/04/18 PHP
Yii2框架数据库简单的增删改查语法小结
2016/08/31 PHP
php实现查询功能(数据访问)
2017/05/23 PHP
详解PHP中的8个魔术常量
2020/07/06 PHP
[原创]提供复制本站内容时出现,该文章转自脚本之家等字样的js代码
2007/03/27 Javascript
JS简单编号生成器实现方法(附demo源码下载)
2016/04/05 Javascript
js从外部获取图片的实现方法
2016/08/05 Javascript
vue-cli 使用vue-bus来全局控制的实例讲解
2018/09/15 Javascript
elementUi vue el-radio 监听选中变化的实例代码
2019/06/28 Javascript
[02:53]DOTA2英雄昆卡基础教程
2013/11/25 DOTA
python获取本机外网ip的方法
2015/04/15 Python
使用python实现rsa算法代码
2016/02/17 Python
Python对文件操作知识汇总
2016/05/15 Python
pandas数据处理基础之筛选指定行或者指定列的数据
2018/05/03 Python
python开发准备工作之配置虚拟环境(非常重要)
2019/02/11 Python
windows下python安装pip方法详解
2020/02/10 Python
解决ROC曲线画出来只有一个点的问题
2020/02/28 Python
pyecharts动态轨迹图的实现示例
2020/04/17 Python
django haystack实现全文检索的示例代码
2020/06/24 Python
西尔斯百货官网:Sears
2016/09/06 全球购物
英国在线药房和在线医生:LloydsPharmacy
2019/10/21 全球购物
Ref与out有什么不同
2012/11/24 面试题
网络教育自我鉴定
2014/02/04 职场文书
代办委托书怎样写
2014/04/08 职场文书
小学教师个人先进事迹材料
2014/05/17 职场文书
作风建设年活动实施方案
2014/10/24 职场文书
幼儿教师小班个人总结
2015/02/05 职场文书
社区三八妇女节活动总结
2015/02/06 职场文书
我们的节日重阳节活动总结
2015/03/24 职场文书
小学教师岗位职责
2015/04/02 职场文书
2016领导干部廉洁自律心得体会
2016/01/13 职场文书
再读《皇帝的新衣》的读后感悟!
2019/08/07 职场文书
Java9新特性之Module模块化编程示例演绎
2022/03/16 Java/Android