精英盒子 -> 程序设计 -> PHP的锁的用处,和我写的几个锁 [打印本页]

jybox 2012-08-11 17:14

PHP的锁的用处,和我写的几个锁

首先我们看一个栗子

用户访问了jb(我是说JyBBS)的一个帖子


1.从数据库取出这个帖子的访问量,存到一个变量中
2.把这个变量加一
3.把这个变量写入到数据库

这看起来很正常,但是,现在一般的web服务器都是多个进程同时运行的,如果恰巧有两个用户同时访问同一个帖子
那么整个过程就会是这样:


用户A:从数据库取出这个帖子的访问量,存到一个变量A中(例如100)
用户B:从数据库取出这个帖子的访问量,存到一个变量B中(也是100)
用户A:把这个变量A加一(这时是101)
用户B:把这个变量B加一(这时是101)
用户A:把这个变量A写入到数据库(这时数据库中是101)
用户B:把这个变量B写入到数据库(这时数据库中还是101)


大家细看一下!用户A写入数据库的结果实际上被覆盖了,访问量只增加了一次!
对于访问量这不重要,但是如果是你去淘宝买东西充值(好吧,其实淘宝用的是java)呢?

---------------------

这种情况下我们就要用到锁,这是在并发(就是多个进程/线程同时运行)编程中很常见和常用的概念

加锁就是,有一个中间人,负责管理各个进程,让他们的工作同步,不会出现例如上面的情况

引入锁之后,上面的过程是这样的:

用户A:询问中间人,我是否可以加锁
中间人:是的,你可以,因为现在并没有被其他人锁定
用户A:从数据库取出这个帖子的访问量,存到一个变量A中(例如100)
用户B:询问中间人,我是否可以加锁
中间人:不,你不可以,因为已经被A加锁了
用户B:好吧,我等着
用户A:把这个变量A加一(这时是101)
用户B:询问中间人,我是否可以加锁
中间人:不,你不可以,因为已经被A加锁了
用户B:好吧,我等着
用户A:把这个变量A写入到数据库(这时数据库中是101)
用户A:告诉中间人,我的工作已经做完了,不需要这个锁了
中间人:OK
用户B:询问中间人,我是否可以加锁
中间人:是的,你可以,因为现在并没有被其他人锁定(锁已经被A释放了)
用户B:从数据库取出这个帖子的访问量,存到一个变量B中(这时是101)
用户B:把这个变量B加一(这时是102)
用户B:把这个变量B写入到数据库(这时数据库中就是102了)
用户B:告诉中间人,我的工作已经做完了,不需要这个锁了
中间人:OK




在ASP、ASP.Net中,都提供了比较简单的加锁接口
但是在坑爹的PHP中,却不这么容易

在PHP中,没有提供加锁的简单接口,我们只能通过取巧,用其他途径实现加锁
有三种可以参考的方式
  • 1.文件锁,建立一个文件,然后对文件加锁(这是操作系统提供的功能),其他进程要想获得锁,比如得到这个文件的锁。这时操作系统会保证同时只有一个进程得到锁
  • 2.MySQL锁,通过MySQL的加锁功能
  • 3.共享内存锁,对内存来加锁,这也是操作系统提供的功能

我实现了前两种锁,分别叫lpFileLock和lpMySQLLock

使用lpFileLock的话,上面的流程会变成这样的代码:


$lock=new lpFileLock("mylock");$lock->lock();//从数据库中得到浏览量$a
$a++;
//把$a写回数据库
$lock->unLock();





上面的代码中还有个lpMutex,它是一个互斥区,为加锁提供了一种更简单的方法:



{
lpMutex $lock;
//从数据库中得到浏览量$a
$a++;
//把$a写回数据库
}


死锁问题
从上面的流程可以看出,一旦得到锁后,一直不释放锁,那么就成了死锁——其他任何人都无法再得到这个锁
所有的工作都卡住了——你的网站也挂了

所以我把锁写成了类,超出作用域,或者脚本执行结束,会自动释放锁



jybox 2012-08-11 17:17
这格式,太坑了

outman 2012-08-11 18:02
一眼扫了几行 觉得和操作系统中的互斥锁比较相似

jybox 2012-08-11 18:06
outman:一眼扫了几行 觉得和操作系统中的互斥锁比较相似[表情]  (2012-08-11 18:02) 

都是一个原理,只不过这里讲的是如何在PHP中使用和实现




Powered by phpwind v8.7 Code ©2003-2011 phpwind
Time 0.042615 second(s),query:5 Gzip enabled