宝哥软件园

php并发锁定示例

编辑:宝哥软件园 来源:互联网 时间:2021-09-07

在工作项目中,会出现一些php并发访问修改数据的问题。如果该数据未被锁定,将导致数据错误。接下来,我将分析一个金融支付锁定问题。希望对大家有帮助。

1未应用锁定机制

1.1金融支付的简化版本代码

!- ?PHP/* * * pay.php * *支付没有应用锁定* *版权所有(c) 2016 * *修改历史: * * 2016/9/10,by clevercode,Create * *///user pays函数pay ($ userid,$ money){ if(false==is _ int($ userid)| | false==is _ int($ money)){ return false;}//取出总金额$ total=getuseleftmoney($ userid);//成本大于剩余if($ money-$ total){ return false;}//余额$ left=$ total-$ money;//更新余额返回setuseleftmoney($ userid,$ left);}//取出用户的余额函数getuseleftmoney($ userid){ if(false==is _ int($ userid)){ return 0;} $sql='选择帐户表单user_account,其中userId=$ { userId } ';//$ MySQL=new MySQL();//mysql数据库返回$ MySQL-query($ SQL);}//更新用户余额函数setuseleftmoney($ userid,$ money){ if(false==is _ int($ userid)| | false==is _ int($ money)){ return false;} $ SQL=' update user _ account set account=$ { money }其中userId=$ { userId } ';//$ MySQL=new MySQL();//mysql数据库返回$ MySQL-execute($ SQL);}?1.2问题分析

如果有两个运营商(P和M),都使用账号100,分别在pc和手机上同时登录,则100个账户的总余额为1000,P运营商支出200,M运营商支出300。并发流程如下。

p操作员:

提取用户余额1000。付款后剩余800=1000-200。更新后的账户余额为800。m操作员:

提取用户余额1000。付款后剩余700=1000-300。付款后账户余额为700。两次付款后,账户余额仍为700元。应该花了500元,账户余额是500元。造成这种现象的根本原因是P和M同时运算得到的平衡数据是1000。

2锁设计

一般只有两步锁,一是拿到锁;第二个是releaseLock。但是现实中锁定的方式有很多,可以通过文件实现;Sql实现;Memcache实现;根据这个场景,我们考虑使用策略模式。

2.1类图设计如下

2.2 php源代码设计如下

LockSystem.php

!- ?PHP/* * * LockSystem.php * * PHP锁机制* *复制权限2016年* *修改历史: * - * 2016/9/10,由CleverCode,Create * */class LockSystem { const LOCK _ TYPE _ DB=' SQLLock ';const LOCK _ TYPE _ FILE=' FIle LOck ';const LOCK _ TYPE _ MEMCACHE=' memcacheLOck ';private $ _ lock=null private static $ _支持lock=array(' FileLock ',' SQLLock ',' MemcacheLock ');public function _ _ construct($ type,$ options=array()){ if(false==empty($ type)){ $ this-createLock($ type,$ options);} }公共函数createLock($type,$ options=array()){ if(false==in _ array($ type,self : $ _ support lock)){ 0抛出新的异常('不支持“${type}”的锁');} $ this-_ lock=new $ type($ options);}公共函数getLock($key,$ time out=iLock : expire){ if(false==$ iLock的this-_ lock实例){ throw new Exception(' false==$ iLock的this-_ lock实例');} $this-_lock-getLock($key,$ time out);}公共函数释放锁($ key){ if(false==$ ILock的this-_ lock实例){抛出新的Exception(' false==$ ILock的this-_ lock实例');} $ this-_ lock-release lock($ key);} }接口iLock { const EXPIRE=5;公共函数getLock($key,$ time out=self : expire);公共函数释放锁($ key);文件锁定类实现了ILock { private $ _ FP private $ _ single public function _ _ construct($ options){ if(isset($ options[' path '])是_ dir($ options[' path ']){ $ this-_ lock path=$ options[' path '],'/';} else { $ this-_ LockPath='/tmp/';} $ this-_ single=isset($ options[' single '])?$ options[' single ']: false;}公共函数getLock($key,$ time out=self : expire){ $ start time=timer : gettimestamp();$file=md5(__FILE__ .$ key);$this-fp=fopen($this-_lockPath .$文件. lock ',' w ';if(true | | $ this-_ single){ $ op=LOCK _ EX LOCK _ NB;} else { $ op=LOCK _ EX } if(false==flock($ this-FP,$ op,$ a))}抛出新的异常('失败');}返回真}公共函数释放LOCK($ key){ flock($ this-FP,LOCK _ UN);fc输($本-FP);}}类SQLLock实现iLock { public function _ _ construct($ options){ $ this-_ db=new MySQL();}公共函数getLock($key,$ time out=self : expire){ $ SQL=' SELECT GET _ LOCK '().$key .', ''.$超时。')';$ RES=$ this-_ db-query($ SQL);返回$ res}公共函数释放锁($ key){ $ SQL=' SELECT RELEASE _ LOCK '().$key .)';返回$ this-_ db-query($ SQL);}}类MemcacheLock实现了ILock { public function _ _ construct($ options){ $ this-Memcache=new Memcache();}公共函数getLock($key,$ time out=self : expire){ $ wait ime=20000;$ TotalWaitime=0;$ time=$ time out * 1000000 while($ totalWaitime $ time false==$ this-memcache-add($ key,1,$ time out)){ us LEEP($ waitime);$ TotalWaitime=$ Waitime } if($ TotalWaitime=$ time)抛出新的异常('无法获得等待锁。$超时. s . ');}公共函数释放lock($ key){ $ this-memcache-delete($ key);}}3 应用锁机制

3.1 支付系统应用锁

!- ?PHP/* * * pay.php * *支付应用锁* *版权所有2016年* *修改历史: * - * 2016/9/10,作者:CleverCode,Create * *///用户支付函数pay($userId,$ money){ if(false==is _ int($ userId)| | false==is _ int($ money)){ return false;}尝试{ //创建锁(推荐使用MemcacheLock)$ lockSystem=new lockSystem(lockSystem : lock _ TYPE _ MEMCACHE);//获取锁$lockKey='pay ' .$ userId $ lockSystem-getLock($ lock key,8);//取出总额$ total=getuseleftmoney($ userId);//花费大于剩余if($ money $ total){ $ ret=false;} else { //余额$ left=$ total-$ money;//更新余额$ ret=setuseleftmoney($ userId,$ left);} //释放锁$ lockSystem-释放锁($ lock key);} catch(异常$e) { //释放锁$ lockSystem-释放锁($ lock key);} }//取出用户的余额函数getuseleftmoney($ UserID){ if(false==is _ int($ UserID)){ return 0;} $sql='选择帐户表单用户帐户,其中userId=$ { userId } ';//$ MySQL=new MySQL();//mysql数据库返回$ MySQL-查询($ SQL);} //更新用户余额函数setuseleftmoney($ userId,$ money){ if(false==is _ int($ userId)| | false==is _ int($ money)){ return false;} $ SQL=' update user _ account set account=$ { money }其中userId=$ { userId } ';//$ MySQL=new MySQL();//mysql数据库返回$ MySQL-执行($ SQL);}?3.2 锁分析

p操作人:

获取锁:支付100英镑取出用户的余额1000。支付后剩余800=1000 - 200。更新后账户余额800。释放锁:支付一亿英镑操作人:

等待锁:支付100英镑获取锁:支付100英镑获取余额:800 支付后剩余500=800 - 300。支付后账户余额500。释放锁:支付100英镑两次支付后,余额500。非常完美了解决了并发造成的临界区资源的访问问题。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多资讯
游戏推荐
更多+