在为台湾公司开发“保证金交易系统”的过程中,出现了这样的情况:

一个间银行有n个操作员,可以同时在系统中下单,系统需要判断银行的保证金是否足够来决定是否可以下单成功。账号保证金足够,正常下单,账号保证金不足则给出提示。

当n个操作员同时操作时,会有这样的情况发生:

分成两个阶段:1、判断保证金 2、进行交易,扣除交易的金额。

A经过第一阶段  判断保证金 足够,B 经过第一阶段 判断保证金 足够。

A进行交易,扣减保证金金额。注意此时 B已经经过判断了,还未进行交易,而此时账户金额变化了。

有可能B的交易金额,大于账号的金额。不满足交易条件。但此时B已经经过了第一阶段的判断,然后系统会将银行的账号扣为负数,B的交易完成。

对于这个问题需要用锁来解决。以下是我搜集的资料:


据说目前PHP并没有完善的线程支持,甚至部署到基于线程模型的httpd服务器都会产生一些问题,但即使是多进程模型下的PHP,也难免出现多进程共同访问同一资源的情况。比如整个程序共享的数据缓存,或者因为资源受限而必须对特定处理过程进行排队,以及针对每个用户生成唯一的某种标识的情形。PHP语言自身没有提供进程互斥和锁定机制,因而使得在这些情况下的编程遇到了困难,目前了解到的可选的办法有以下这些:

  • 利用MySQL的锁定机制来实现互斥。缺点是增大了数据库服务器的连接负担,并且使得程序依赖于数据库服务才能正常工作。
  • 利用文件锁机制。也就是利用flock函数通过文件实现锁定和互斥机制,来模拟通用编程模型下的锁定原语的工作方式。这种方式在以前以纯文本文件为存储引擎的时代成为保护数据完整性的必备元素,现在在使用文本文件作为缓存媒介的场合也相当常见。PmWiki应该也是使用了这个机制来对多人同时编辑一个页面的情形进行提醒。不过文件锁机制多少会调用到宿主操作系统上的文件锁特性,因此在使用时一定要检查服务器操作系统是否为PHP环境提供了完善可靠的文件锁机制。
  • 利用共享内存空间计数。PHP可以利用shmop_open函数开辟一块内存空间,在服务进程之间共享数据,为了保证共享数据的互斥安全访问,可以使用sem_get、sem_acquire和sem_release这组函数实现共享计数锁定机制。这种办法在后台实际是调用了系统的ipc服务来实现

有这么一个需求:生成文件的时候,由于多用户都有权限进行生成,防止并发下,导致生成的结果出现错误,需要对生成的过程进行加锁,只容许一个用户在一个时间内进行操作,这个时候就需要用到锁了,将这个操作过程锁起来。在用了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 key
private $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)
{
$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();
//使用过程中需要注意下文件锁所在路径需要有写权限.

黏贴完了。下面自己写的测试文件:

$handle = fopen("t.txt","r+");
if($handle!==false){ //打开成功
flock($handle, LOCK_EX);
for($i=0;$i<100;$i++){
echo $i;
sleep(1);
}
flock($handle,LOCK_UN);
fclose($handle);
}else{
echo 'error';
}

可以打开两个cmd进行测试,结果必须第一个输入完成后,第二个cmd才会有输出,程序阻塞。结果:

E:\htdocs\mytest\lab>php.exe t.php
01234567891011121314151617181920212223242526272829303132333435363738394041424344
45464748495051525354555657585960616263646566676869707172737475767778798081828384
858687888990919293949596979899

这里有一个不友好的方面,如果锁里面的代码执行时间太长,那么其他用户一直被阻塞,不能得到系统的提示!

这里有更好的解决方法:http://blog.163.com/lgh_2002/blog/static/4401752620114411329424/

感谢 javascript罗浮宫群里的vi。

php代码锁的更多相关文章

  1. synchronized锁机制 之 代码块锁(转)

    synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用 ...

  2. java 多线程9 : synchronized锁机制 之 代码块锁

    synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间.这种情况下可以尝试使用 ...

  3. java内置锁实现锁住代码块方案(同一个对象或锁住整个类.class)

    我们看一个例子: class Demo { public synchronized void test() { System.out.println("test方法开始执行,当前线程为:&q ...

  4. java多线程(三)——锁机制synchronized(同步语句块)

    用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...

  5. Java多线程(二) 多线程的锁机制

    当两条线程同时访问一个类的时候,可能会带来一些问题.并发线程重入可能会带来内存泄漏.程序不可控等等.不管是线程间的通讯还是线程共享数据都需要使用Java的锁机制控制并发代码产生的问题.本篇总结主要著名 ...

  6. java线程总结3--synchronized关键字,原理以及相关的锁

    在多线程编程中,synchronized关键字非常常见,当我们需要进行"同步"操作时,我们很多时候需要该该关键字对代码块或者方法进行锁定.被synchronized锁定的代码块,只 ...

  7. 转 Java并发之锁的升级

    说明:本文大部分内容来自<并发编程的艺术>,再加上自己网络整理和理解 以下内容来自<java并发编程的艺术>作者:方鹏飞 魏鹏 程晓明 在多线程并发编程中synchronize ...

  8. javaWeb锁屏的简单实现

    简单介绍:需求上有个小功能,用户登录后点击用户名,可以点击锁屏,锁屏解除,需要输入正确的密码才能进入管理后台页面enheng(*/ω\*)(*/ω\*)(*/ω\*) 思路简介:其实刚看到要做锁屏,第 ...

  9. Java并发之彻底搞懂偏向锁升级为轻量级锁

    网上有许多讲偏向锁,轻量级锁的文章,但对偏向锁如何升级讲的不够明白,有些文章还相互矛盾,经过对jvm源码(biasedLocking.cpp)的仔细分析和追踪,基本升级过程有了一个清晰的过程,现将升级 ...

随机推荐

  1. Identity-第三章 Authorize原理解析

    本篇旨在解析Identity中角色限制的原理. 需要的工具:Visual Studio.Reflector 问题提出: 1.当我们需要限制某个Controller的名称只需要某个或者某几个角色访问,这 ...

  2. 【转】解决Cannot change version of project facet Dynamic web module to 2.5

    http://blog.csdn.net/steveguoshao/article/details/38414145 我们用Eclipse创建Maven结构的web项目的时候选择了Artifact I ...

  3. jQuery Mobile方向感应事件

    在现在的智能手机中,都有对方向变换的自动感知功能,比如当手机方向从水平方向切换到垂直方向时,则会触发该事件.在jQuery Mobile中,可以通过orientationchange事件进行绑定,并且 ...

  4. 分布式助手Zookeeper(三)

    分布式助手Zookeeper(三)博客分类: Zookeeper zookeeperapi操作zookeeper 本篇,散仙要介绍一下基于zookeeper的一些API的编程. 在此之前,我们先来熟悉 ...

  5. leecode 每日解题思路 102-Binary Tree Level Order Traversal

    題目描述: 题目链接: 102-Binary Tree Level Order Traversal 这个问题要解决的是如何逐层遍历一个二叉树,并把同一层元素放入同一list中, 再将所有元素返回. 其 ...

  6. Add an IPv6 route through an interface

    Not often needed, sometimes in cases of dedicated point-to-point links. 7.4.1. Using "ip" ...

  7. update 改写 merge into

    update语句改写成merge into有时会提高运行速度 看两个案例 1.根据业务将两个嵌套子查询改写成max,速度有3min提升到3s UPDATE OPER_792.LL_SCB_YDKB_2 ...

  8. c#的方法重写和的java方法重写有什么区别

    java code: package example; class m1 { public int getInt() { return 0; } } class m2 extends m1 { pub ...

  9. jsp页面中frameset的使用方法

    frame,是网页开发必须掌握的知识.例如后台架构.局部刷新,页面分割,都是frame的用途表现,尤其是后台页面制作,使用frame会给用户带来非常舒适的使用感受. frame知识点包括(frames ...

  10. 结合Retrofit,RxJava,Okhttp,FastJson的网络框架RRO

    Retrofit以其灵活的调用形式, 强大的扩展性著称. 随着RxAndroid的推出, Retrofit这样的可插拔式的网络框架因其可以灵活兼容各种数据解析器, 回调形式(主要还是RxJava啦)而 ...