php学习之并发控制中的独占锁

php开发中,我们常常遇到并发问题,那对于并发控制中的独占锁问题我们怎么解决呢?一起来看看吧。

1.并发问题

并发大家都知道是什么情况,这里说的是并发多个请求抢占同一个资源,直接上实例吧

请求:index.php?mod=a&action=b&taskid=6

处理:

$key = "a_b::".$uid.’_’.$taskid;

$v = $redis->get($key);

if($v == 1){

$redis->setex($key,10,1);

//处理逻辑省略

}

2.分析

逻辑看来还可以,结果发现数据库中写入了两个同样的请求结果,我看了记录的时间戳,天!居然是同一秒.

我用microtime(true) log一下两个请求的时间差居然相差了0.0001s,就是说$redis->setex($key,10,1);还没执行成功 第二个请求已经get到跟第一个请求一样的结果。这不就是传说中的并发抢占资源。这中情况 听过很多,在开发过程中也没刻意去模拟实验过。

3.解决

方案1:第一反应就是要给处理过程加事务(数据库是MySQL innoDB),加事务的结果就是 第一个请求成功了 第二个请求会执行到后面捡查发现重了会回滚。其实mysql事务在保证数据一致性上是很ok的,但是通过回滚来保证唯一资源独占代价太大,做过mysql事务测试测同学都知道,事务中的insert是已经插进去了,回滚之后才删掉的。

方案2:还有一个选择就是php中的文件独占锁,那就是说这情况下我要新建 用户数 * 任务数的文件来实现每个请求资源的独占,如果独占资源较少的话可选的解决办法:

/**

* 加锁

*/

public function file_lock($filename){

$fp_key = sha1($filename);

$this->fps[$fp_key] = fopen($filename, ’w+’);

if($this->fps[$fp_key]){

return flock($this->fps[$fp_key], LOCK_EX|LOCK_NB);

}

return false;

}

/**

* 解锁

*/

public function file_unlock($filename){

$fp_key = sha1($filename);

if($this->fps[$fp_key] ){

flock($this->fps[$fp_key] , LOCK_UN);

fclose($this->fps[$fp_key] );

}

}

方案3:发现$redis->setnx()可以提供原子操作的状态:相同的key执行setnx之后没过期或者没del,再执行会返回false。这就让两个以上的并发请求得到控制必须成功获取锁才能继续。

/**

*  加锁

*/

public function task_lock($taskid){

$expire = 2;

$lock_key =’task_get_reward_’.$this->uid.’_’.$taskid;

$lock = $this->redis->setNX($lock_key , time());//设当前时间

if($lock){

$this->redis->expire($lock_key,  $expire); //如果没执行完 2s锁失效

}

if(!$lock){//如果获取锁失败 检查时间

$time = $this->redis->get($lock_key);

if(time() - $time  >=  $expire){//添加时间戳判断为了避免expire执行失败导致死锁 当然可以用redis自带的事务来保证

$this->redis->rm($lock_key);

}

$lock =  $this->redis->setNX($lock_key , time());

if($lock){

$this->redis->expire($lock_key,  $expire); //如果没执行完 2s锁失效

}

}

return $lock;

}

/**

*  解锁

*/

public function task_unlock($taskid){

$this->set_redis();

$lock_key = ’task_get_reward_’.$this->uid.’_’.$taskid;

$this->redis->rm($lock_key);

}

说明下setNX 和expire 这两个操作其实可以用redis事务来保证一致性

文章来源:魅族科技开发团队

时间: 2024-08-26 00:03:03

php学习之并发控制中的独占锁的相关文章

多线程编程学习六(Java 中的阻塞队列).

介绍 阻塞队列(BlockingQueue)是指当队列满时,队列会阻塞插入元素的线程,直到队列不满:当队列空时,队列会阻塞获得元素的线程,直到队列变非空.阻塞队列就是生产者用来存放元素.消费者用来获取元素的容器. 当线程 插入/获取 动作由于队列 满/空 阻塞后,队列也提供了一些机制去处理,或抛出异常,或返回特殊值,或者线程一直等待... 方法/处理方式 抛出异常 返回特殊值 一直阻塞 超时退出 插入方法 add(e) offer(e) put(e) offer(e, timeout, unit

基于windows api实现的共享锁/独占锁

众所周知,windows平台上实现线程同步.或者说资源的加锁与解锁的方法有内核事件.临界区.相互排斥量.信号量,甚至interlocked系列函数等多种手段. 可是在日常的编程中,我们使用这些手段对 "多个线程同一时候对同一个资源进行读写" 的时候,在读写之前先要对资源假锁,读写完之后要对资源解锁. 设想这样一种情况,有一个ftpserver.每天有非常频繁的对这个ftp服务的文件进行下载,可是差点儿好几天才会对这些文件进行更新.在我们每一次对文件下载的时候,读取文件的时候都要对文件进

hibernate中的乐观锁和悲观锁

hibernate支持两种锁:悲观锁(Pessimistic Locking)和乐观锁(Optimistic Locking) 悲观锁:指的是对数据库数据被外界的修改持保守态度(无论是本系统的事务处理,或者是外部系统的事务处理),在整个数据处理的过程数据都处于锁定的状态.hibernate中的悲观锁,是依靠数据库中的锁机制(因为只有数据库层才能控制本系统和外部系统对数据库的数据操作). 例如"select * from user where userName='Johnson' for upda

数据库中的悲观锁和乐观锁详解

数据中的锁分为两类:悲观锁和乐观锁,锁还有表级锁.行级锁 表级锁例如: SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除 SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除 行级锁例如: select * from table_name where id = 1 for update; 悲观锁(Pressimistic Locking) 对数据被外界(包括本系统当前的其他事务,以及来自

TFS2013 设置签出独占锁(转载)

作者:晓菜鸟 出处:http://www.cnblogs.com/52XF/p/4239056.html 在使用TFS进行源代码管理的时候VS默认允许多个签出,但在团队开发中往往需要设置独占锁(排他锁)避免冲突,设置独占锁不需要在TFS的网页里面进行设置,只需要在VS里面进行设置即可. 一.取消启用多个签出 1.打开VS2013--团队--团队项目设置--源控件--签出设置--去掉"启用多个签出"的勾选.这里注意一下"启用在签出时获取最新版本"这个选项默认是不被勾选

在 Java 中高效使用锁的技巧--转载

竞争锁是造成多线程应用程序性能瓶颈的主要原因 区分竞争锁和非竞争锁对性能的影响非常重要.如果一个锁自始至终只被一个线程使用,那么 JVM 有能力优化它带来的绝大部分损耗.如果一个锁被多个线程使用过,但是在任意时刻,都只有一个线程尝试获取锁,那么它的开销要大一些.我们将以上两种锁称为非竞争锁.而对性能影响最严重的情况出现在多个线程同时尝试获取锁时.这种情况是 JVM 无法优化的,而且通常会发生从用户态到内核态的切换.现代 JVM 已对非竞争锁做了很多优化,使它几乎不会对性能造成影响.常见的优化有以

数据库中的 各种锁 详解

1 前言 数据库大并发操作要考虑死锁和锁的性能问题.看到网上大多语焉不详(尤其更新锁),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另一个线程.T3,T4以此类推.下面以SQL Server(2005)为例. 2 锁的种类 共享锁(Shared lock). 例1: ---------------------------------------- T1: select * from table (请想象它需要执行

[Android学习笔记]Android中多线程开发的一些概念

线程安全: 在多线程的情况下,不会因为线程之间的操作而导致数据错误. 线程同步: 同一个资源,可能在同一时间被多个线程操作,这样会导致数据错误.这是一个现象,也是一个问题,而研究如何解决此类问题的相关工作就叫做线程同步. android中,处理线程同步的手段就是:锁 一般分为公平锁和非公平锁: synchronized(内部锁,互斥锁):synchronized是JVM提供的线程同步机制,如果出现问题,JVM能捕获异常,并释放资源,具体实现机制需要查看JVM源码 synchronized的使用特

TFS2013 设置签出独占锁

在使用TFS进行源代码管理的时候VS默认允许多个签出,但在团队开发中往往需要设置独占锁(排他锁)避免冲突,设置独占锁不需要在TFS的网页里面进行设置,只需要在VS里面进行设置即可. 一.取消启用多个签出 1.打开VS2013--团队--团队项目设置--源控件--签出设置--去掉"启用多个签出"的勾选.这里注意一下"启用在签出时获取最新版本"这个选项默认是不被勾选的,为了方便,我们将其勾选上,以后再签出代码时系统就会自动获取最新的版本了,点击"确定"