户A打开应用的界面,看到数据库的某条记录
b用户B打开应用的界面,看到同样一条记录
c 用户A对记录做了修改
d 对于web应用而言[假设没有应用comet类似技术],通常B不知道这个修改,这时B也对同样这条记录做修改,那B就有可能覆盖A做的修改;
这个问题在数据库中被称为丢失更新问题
2我自己对这个问题的理解过程是这样的:
a 不知道这个问题
我在做开发好长时间之后才意识到这个问题,意识到这个问题之后,我后来发现很长一段时间内都没真正搞明白为什么这是个问题-_- 而且我发现现在周围的很多同事,尤其是新毕业的学生,其实也一直过了很长时间都没明白这个问题,这说明吧不知道这个丢失更新问题是一个非常普遍的问题:)
b用信号量以及 *** 作之前再次验证的方法解决
最开始的时候,测试发现了这样一个问题,要求解决,我把 *** 作系统的教科书搬来,对照着写了一个信号量semaphore类[那时候还是jdk 142,jdk里面没有concurrent包],花了好长时间测试这个semaphore的实现是正确的[重复发明轮子的血泪史],
然后用来控制这个 *** 作,每次 *** 作前获取信号量,然后验证,再做真正的数据库 *** 作。。。相当于在应用层每次都只做一件事。
c 再次理解
再后来,我看了Tom的这本9i和10g的书,书中提到前面的丢失更新过程,大概有点明白为什么这是个问题
8a7315603d51c12ef97a82729cdc4677png
但是其实我有个疑问,对于数据库中的记录而言,A做的修改本来就有可能被B覆盖的,为什么这会是一个丢失更新问题呢 正好项目里面又出现了类似的情况,我仔细观察了下,终于明白为什么这是个问题,以及为什么要使用对应的乐观锁悲观锁方案了。下面对此做详细说明
3 一个比较清楚的场景
下面这个假设的实际场景可以比较清楚的帮助我们理解这个问题:
假设当当网上用户下单买了本书,这时数据库中有条订单号为001的订单,其中有个status字段是’有效’,表示该订单是有效的;
后台管理人员查询到这条001的订单,并且看到状态是有效的
用户发现下单的时候下错了,于是撤销订单,假设运行这样一条SQL: update order_table set status = ‘取消’ where order_id = 001;
后台管理人员由于在b这步看到状态有效的,这时,虽然用户在c这步已经撤销了订单,可是管理人员并未刷新界面,看到的订单状态还是有效的,于是点击”发货”按钮,将该订单发到物流部门,同时运行类似如下SQL,将订单状态改成已发货:update order_table set status = ‘已发货’ where order_id = 001
如果当当的系统这样实现,显然不对了,肯定要挨骂了,明明已经取消了订单,为什么还会发货呢而且确实取消订单的 *** 作发生在发货 *** 作之前啊。 因为在这样的实现下,后台管理人员无论怎么做都有可能会出错,因为他打开系统看到有效的订单和他点发货之间肯定有个时间差,在这个时间差的时候总是存在用户取消订单的可能。
4 当时的详细解决方法。几年前当测试人员告诉我系统存在这个问题的时候,我的解决方法是这样的, 首先,先把 *** 作系统的教科书搬来,然后对照着了一个semaphore,然后反复测试各种情况证明写的是正确的; 然后,
1 获取一个信号量,保证每次只能有一个线程进入下面的步骤
2 检查数据库,看这条订单是否状态是有效的
a 如果有效则继续,进入发货步骤 b) 如果无效则返回,释放信号量,告诉用户状态已经发生改变
3 发货,释放信号量
看到这里,也许很多人要骂我蠢了,直接把SQL语句改成下面这样吧就可以了么 update order_table set status = ‘已发货’ where order_id = 001 and status = ‘有效’ 是的,的确是这样。虽然我当时的项目的情况比和这个稍微复杂一点,涉及到多张表格,不能直接这么做,但当时的确不知道这个更新丢失问题,也没想到合适的类似方式,于是就在应用层做了这么一个每次实际上只能有一个用户在做真正的更新这样一个方式来解决,这样做的结果是,在应用层单独做了类似这么一个锁的机制。我记得当时的项目毕业答辩的时候,老师问我同步的这个问题不直接用数据库的锁的方案来解决我当时胡乱回答了下,后来想起来,其实压根没理解老师的意思-_- 而且这样做有一个问题,假设在特殊情况下,这条订单被DBA直接修改了,没有经过应用,那么应用做这个 *** 作也会是错的,因为在2a到3之前的这段时间,有可能正好是DBA直接修改的时候。那么3做的 *** 作也是不对的。 而且,现实情况是在后来的几年开发过程中,我也的确在一些不同的项目代码中看到,其他很多人也在使用类似的代码解决测试人员告诉他们的这些同步问题-_-
5正确而简洁的解决方法
问题清楚了,也说明了我曾经使用的解决方案也是一个简洁直接的解决方案,纯粹是把简单问题复杂化,下面说说实际有效的解决方案; 就这个丢失更新问题,可以通过数据库的锁来实现,基本两种思路,一种是悲观锁,另外一种是乐观锁; 简单的说就是一种假定这样的问题是高概率的,最好一开始就锁住,免得更新老是失败;另外一种假定这样的问题是小概率的,最后一步做更新的时候再锁住,免得锁住时间太长影响其他人做有关 *** 作;
6 乐观锁的方法
这里先说web开发中常用的乐观锁的方法:
1很简单,就是使用前面所说的这样一条SQL,这其实是所谓使用”前镜像”的方式来保证需要更新的数据是符合要求的,
update order_table set status = ‘已发货’ where order_id = 001 and status = ‘有效’ Tom的书上举的例子是对所有列做更新,所以他的SQL大致如下 Update table set col1 = newcol1value, col2 = newcol2value… where col1 = oldcol1value and col2 = oldcol2value… 这个我觉得需要根据应用具体分析,如果需要判断所有的值,那就判断所有的值,如果只关心其中一个或部分值,那只需要取相关的值就好了,就比如这里的订单的状态
2使用版本列[比如时间戳
这个方法比较简单,也最常用,就是在数据库表格中加一列last_modified_date,就是最后更新的时间,每次更新的时候都将这列设成systimestamp,当前系统时间;
然后每次更新的时候,就改成这样 Update table set col = newvalue where id = and last_modified_date = old last_modified_date 这样,就可以检验出数据库的值是否在上次查看和这次更新的时候发生了变化,如果发生了变化,那么last_modified_date就变化了,以后的更新就会返回更新了0行,系统就可以通知用户数据发生了变化,然后选择刷新数据或者其他流程。
至于这个last_modified_date的维护,可以选择让应用每次都维护这个值,或者是使用存储过程来包装更新的 *** 作,或者是使用触发器来更新相关的值。几种方法各有利弊,比如应用维护需要保证每段相关代码都正确的维护了这个值;存储过程有一定的开销,通常很多开发对写存储过程可能也不熟练;触发器是简单的实现,但是也是有开销的。具体使用哪种方法需要根据实际情况具体取舍。
3使用校验或Hash值
这种方法和前面的方法类似,无非是根据其他有实际意义的列来计算出一个虚拟的列,我个人觉得TOM在介绍这个纯粹是介绍了一种”奇技*巧”,反正我是在实际过程中不知道哪里会需要这样的解决方案,或许也是因为我知道的太少了吧:)
4使用Oracle 10g的ORA_ROWSCN
这个就是利用10g的一个ora_rowscn特性,可以对每行做精确追踪,不过这个要求在create table的时候就指定相关参数,表格如果创建了以后就不能用alter table来修改了,因为这依赖于物理的实际存储。 同样,我觉得这也可以归为”奇技*巧”一类; 具体如果有兴趣了解详情的话,可以参考Tom的书
我们一直都在努力坚持原创请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。
微信公众号
TechTarget
官方微博
TechTarget中国
相关资源:oracle乐观锁和悲观锁详细教程_oracle的乐观锁-Oracle文档类资源
点击阅读全文
打开CSDN,阅读体验更佳
Oracle数据库悲观锁与乐观锁_diweikang的博客
注:对于悲观锁是针对并发的可能性比较大,而一般在我们的应用中用乐观锁足以。 Oracle的悲观锁需要利用一条现有的连接,分成两种方式,从SQL语句的区别来看,就是一种是for update,一种是for update nowait的形式。 1 执行select xxx
ORACLE悲观锁和乐观锁_hongwei3344661的博客
1、无论是选择悲观锁策略,还是乐观锁策略。如果一个对象被上了锁,那么该对象都会受这个锁的控制和影响。 2、选择悲观锁策略,还是乐观锁策略,这主要是由应用和业务需求来确定的。如果你的应用和业务经常会出现从我看到要修改的记录的
oracle 乐观锁和悲观锁详细教程
详细介绍了Oracle中乐观锁、悲观锁的原理及应用,并有实例
基于ORACLE的乐观锁实现原理
2019独角兽企业重金招聘Python工程师标准>>>
继续访问
Oracle之悲观锁和乐观锁_hys21的博客
根据保护的对象不同,Oracle数据库锁可以分为以下几大类:DML锁(data locks,数据锁),用于实现并发存取并保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks
oracle乐观锁和悲观锁详细教程_oracle的乐观锁-Oracle文档类资源
内部包含oracle百度网盘下载链接以及密码。 ocidll 12版本全部 资源是从Oracle官方网站下载,已测试可用 白雪红叶JAVA学习技术栈梳理思维导图xmind 乐观锁行级锁 分布式锁 分区排队 一致性 一致性算法 paxos zab nwr raft gossip
Oracle创建悲观锁和乐观锁
为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突。为了解决这个问题,大多数数据库用的方法就是数据的锁定。 考虑下面的情况。如果我们先查询到数据,然后更新数据。这样会出现这样的情况。A线程查询的时候,B线程也在查询,当A线程准备更新的时候,B线程先获得 了更新锁,将这些行锁定了。A只能等待B更新完。当B线程更新完释放锁的时候,A获得锁,这时A会识别出字段已经
继续访问
Oracle并发控制中的乐观锁
数据库的管理员要分散他们的数据库,以便处理基于Web,B2B,电子商务的访问,快速的硬盘读写以及更多的资源或许只能解决一部分问题。疲乏的锁机制甚至会削弱拥有很好资源的应用性能。乐观锁可以大大改善具有较多事务处理的数据库载入性能,比如基于web的客户端访问。 悲观锁引发的问题: 大多数Oracle开发者已经非常熟悉悲观锁,即在对数据进行更新之前给数据加锁。使用熟悉的SELECTFOR UP
继续访问
oracle乐观锁悲观锁学习笔记(更新中)_EvaronZ的博客
首先解释下乐观锁和悲观锁的含义 乐观锁:乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回错误的信息。 悲观锁:悲观锁就是对数据的冲突采取一种悲观的
Oracle乐观锁和悲观锁_◣NSD◥的博客_oracle悲观锁
乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。 悲观锁
Oracle乐观锁悲观锁
1乐观锁 当处理对象状态时为了防止冲突 例:一个下订单的状态status a更新status为1购买,b取得status为1,这时a要退货把status改为2 这时如果b还按1的状态去处理,发货了。就出错了。 正确的做法为: 当b发货时,为了处理并发脏读,需要先根据原status状态去更新status为3订单处理中 int res = update
继续访问
转 Oracle中乐观锁定的四种实现方式
<br />Oracle中乐观锁定的四种实现方式<br /> <br />转自 >
你获得的信号当然是电压。但它的前面到底是什么,得看前头的线路。如果前面用的是温度传感器,这个原始的量就是温度,如果它是由压力传感器来的,它就是压力。后面的说我不用说了,你该知道了吧。。。
void down(struct semaphore sem); //不可中断
int down_interruptible(struct semaphore sem);//可中断
int down_killable(struct semaphore sem);//睡眠的进程可以因为受到致命信号而被唤醒,中断获取信号量的 *** 作。
int down_trylock(struct semaphore sem);//试图获取信号量,若无法获得则直接返回1而不睡眠。返回0则 表示获取到了信号量
int down_timeout(struct semaphore sem,long jiffies);//表示睡眠时间是有限制的,如果在jiffies指明的时间到期时仍然无法获得信号量,则将返回错误码。
ptcb-;OSTCBY;OSTCBPrio = pip;
OSRdyTbl[ptcb-,还没有释放,应该在ospendxxx函数里面
查看OSMutexPend函数,当释放信号量的任务的优先级为等于互斥信号量最高可提升优先级的时候; 4u) 0x0Fu),重新设置任务就绪表;= pip)
如果当前请求信号量的任务的优先级高于最高提升优先级(数值上低于);OSTCBPrio) {
y = ptcb-,接下来
ptcb-;
ptcb-,然后重新设置任务的原始优先级以及当前任务优先级(释放信号量和申请信号量的是同一个任务),但是, prio)
到这里问题就发生了;OSTCBPrio = prio,保护该资源的信号量为互斥信号量,这个值也就是系统能够将等待该互斥信号量的任务提升的最高优先级
当一个任务请求信号量的时候,b优先级高于c;OSTCBY] = ptcb-;OSTCBY = (INT8U)( ptcb-,b;
if (OSRdyTbl[y] == 0u) {
OSRdyGrp = (OS_PRIO)~ptcb-,在这种故障极大地降低了系统的实时性
以上说的情况就是 *** 作系统的优先级反转
而ucos为了解决这种问题,并把快速访问就绪表的元素的数据改变
既然优先级能被提升;
#if OS_LOWEST_PRIO lt;,发现其中果然有玄机;OSTCBY] = ptcb-
我们假设有三个任务a,他的基本思想是;OSTCBY)
这样:让当前获得互斥信号量的任务的优先级短暂提升到系统可以接受的最大优先级,将提升优先级的任务的新优先级在任务就绪表中设置成就绪,那么也应该要能被降下来;OSTCBX),然后设置快速访问就绪任务表的数据元素;OSTCBBitY,当任务c在运行并准备释放信号量的时候;OSTCBBitY; ptcb-;
}
也就是说;OSTCBX = (INT8U)(prio 0x07u);
}
}
OSTCBPrioTbl[pip] = ptcb;lt,没必要提升优先级;
OSRdyGrp = ptcb-;
pevent-;
perr = OS_ERR_NONE;
流程与之前提权其实是类似的,就说明白了互斥信号量的优先级提升流程;
perr = OS_ERR_PIP_LOWER;
OSTCBPrioTbl[prio] = ptcb;OSEventCnt = OSTCBCur-就是保存在这里;OSTCBBitX = (OS_PRIO)(1uL lt;
if ((INT8U)(pevent-;OSTCBX = (INT8U) (prio 0x0Fu);
OSRdyTbl[y] = (OS_PRIO)~ptcb-,而a能打断b的运行但是需要信号量,c;
将当前任务的优先级切换成提升优先级,接下来我们来看看代码;OSTCBBitY = (OS_PRIO)(1uL lt
到这里,可以看到
if (OSTCBCur-;OSTCBPrio 0x07u),而这个降下来应该需要依靠ospostxxx在释放信号量的时候执行,任务的优先级就被提升了;OSTCBY] = ptcb-;OSTCBY = (INT8U)((INT8U)(prio ,此时任务a开始运行,当a去访问资源s的时候,将系统就绪表中的原来的ready标志清除掉,a优先级高于b;OSTCBPrio lt;OSEventCnt = OSTCBCur-;
ptcb-,所以必须释放以等待信号量, OSEventCnt的低八位
pevent-;
if (rdy == OS_TRUE) {
OSRdyGrp = ptcb-;
} else {
pevent2 = ptcb-;OSTCBBitY,在tcb表中对应pip的位置,在互斥信号量中引入了优先级提升的方法;OSTCBBitY;OSTCBPrio ,如下
pip = (INT8U)(pevent-;
}
Pip变量是保存在OSEventCnt中的;OSTCBPrio ;OSEventCnt ;lt;;= pip) {
OS_EXIT_CRITICAL();
ptcb-,对于实时 *** 作系统而言,优先级比较高的a在优先级比较低的b运行的时候无法抢断;OSTCBBitX,
假设当前任务c申请了信号量访问s,这种情况叫做优先级反转,将当前请求信号量的任务的优先级放到OSEventCnt的低八位中,这样就实现了任务的恢复;
ptcb-;OSTCBBitX) ,最后将tcb数组中对应原始优先级的数据指针设置到指向任务tcb;OSTCBBitX,这样; pip) {
if (mprio ;lt;
OSPrioCur = prio,系统下一次被调用的时候,集中看优先级提升的部分;
if (OSTCBCur-,这个时候系统就只有b在运行,任务b开始运行; ptcb-;lt;OSTCBPrio;OSTCBBitX!= 0u) {
OSRdyTbl[y] = (OS_PRIO)~ptcb-,因为得不到信号量,下面我们来说说优先级反转的典型环境,
if (OSTCBCur-,否则的话;,优先级的提升我们可以猜测应该在一个任务在获取了信号量之后完成的,那么a就会剥夺c的运行而运行a,同时修改系统就绪表;OSTCBY;OSTCBBitY;OSEventTbl[ptcb-,只是一个换成高优先级一个换成低优先级
先将高优先级的任务的任务就绪表中对应的位清除; 3u) 0x07u);
这一段的意思就是当优先级低于最高可提升优先级的时候;
ptcb-;OSTCBX),信号量的设计也是为了满足这个功能;
ptcb = (OS_TCB )(pevent-;OSTCBY),到这里流程都是正常的;
if (ptcb-,prio的来源是从事件中获取的优先级,尽量让该任务快速的完成并释放信号量,可是c优先级比较低得不到运行; ptcb-
原理说完了,那也就是说;OSTCBY = (INT8U)((INT8U)(prio ,那直接运行,a和c都需要访问一个共享资源s,就会按照被提升了优先级的任务的新优先级来进行调度;
ptcb-;OSTCBX = (INT8U)( ptcb-;OSTCBPrio == pip) { OSMutex_RdyAtPrio(OSTCBCur,当我们创建信号量的时候,可剥夺性内核却剥夺不了,我们查看OSMutexPost代码;= 63u
ptcb-; ptcb-;OSEventPtr = (void )OSTCBCur;OSTCBBitX; OSTCBCur-;
if (pevent2 ; 8u),系统故障;OSEventCnt OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {
pevent-,释放之后在恢复为任务原来的优先级别,设置为提升了优先级的任务的tcb,核心代码为
y = ptcb-,那么任务b就要剥夺任务c的运行;OSTCBEventPtr,就要进行下面的 *** 作
mprio = (INT8U)(pevent-;
恢复优先级的函数为OSMutex_RdyAtPrio,任务c得以重新运行;OSEventCnt = OS_MUTEX_KEEP_UPPER_8;
#endif
ptcb-;
#else
ptcb-,如果有信号量空余,之前已经说过互斥信号量的部分实现;OSTCBBitX,需要通过OSMutex_RdyAtPrio函数来将任务的优先级恢复;
if ((OSRdyTbl[y] ptcb-,当任务以独占方式使用共享资源的时候;
pevent2-;
if (OSRdyTbl[y] == 0u) {
OSRdyGrp = (OS_PRIO)~ptcb-;;OSEventGrp = ptcb-;OSTCBPrio lt,就会给定这个值;OSEventCnt OS_MUTEX_KEEP_LOWER_8)!= (OS_EVENT )0) {
pevent2-,那部分不再赘述;
OSRdyTbl[ptcb-;
}
rdy = OS_TRUE;
}
ptcb-;
} else {
OS_EXIT_CRITICAL();OSTCBBitY = (OS_PRIO)(1uL lt,会出现低优先级任务高于高优先级任务运行的情况,如果使用互斥信号量的优先级提升;OSEventPtr),大家还记得记得提升之前的优先级保存到了哪里,a就只能等到b运行完主动释放使用权才能得到运行了,这是一场灾难,最后,因为ucos不允许两个任务有相同的优先级;OSTCBPrio;
pevent-; 3u),那么那个可提升最高优先级必须不能对应有相应的用户任务;OSTCBBitX = (OS_PRIO)(1uL lt,要注意一点在可剥夺性的内核中
一.信号量的概念
是实现任务互斥、同步 *** 作的主要机制。VxWorks提供的信号量经过了高度优化,在所有任务间通信机制中,速度最快。
二.信号量的分类
Binary Semaphores(二进制):完成互斥、同步 *** 作的最佳方式;速度最快,最常用。
Mutual Exclusion Semaphores(互斥):一种特殊的二进制信号量,专门针对互斥 *** 作进行了优化。
Counting Semaphores(计数):类似于二进制信号量,可记录信号量释放的次数,可监视同一资源上的多个实例。
三.Binary Semaphores(二进制信号量)
Task经常会等待事件或需获取资源。查询(polling)在RealTime系统中原则上不允许,最好采用Pending,等待事件或资源。
状态图:
说明:
1为某个资源调用semBCreate()创建一个binary semaphore 并规定:
SEM_Full (资源可用) SEM_Empty (资源不可用)
2资源不可用时Task调用semTake()进入Pending直到semaphore被Given
相关函数:
[c-sharp] view plaincopy
SEM_ID semBCreate
(
int options, / semaphore options /
SEM_B_STATE initialState / initial semaphore state /
)
STATUS semTake
(
SEM_ID semId, / 需要获取的信号量ID /
int timeout / 超时时间(tick)/no-wait/forever /
)
ISR(中断服务程序)不能调用semTake() *** 作!
[c-sharp] view plaincopy
STATUS semGive
(
SEM_ID semId / 需要释放的信号量ID /
)
[c-sharp] view plaincopy
semFlush()
应用方向:
1互斥 *** 作:是指不同任务可以利用信号量互斥地访问临界资源。这种互斥的访问方式比中断禁止(interrupt disable) 与优先级锁定
(preemptive locks)两种互斥方式具有更加精确的粒度。
互斥 *** 作时初始状态设为(SEM_FULL)可用。并在同一个Task中成对、顺序调用semTake()、semGive()。
2同步 *** 作:是指一个任务可以利用信号量控制自己的执行进度,使自己同步于一组外部事件。同步 *** 作时初始状态设为(SEM_EMPTY)不可用。在不同Task中分别单独调用semTake()、semGive()。
四.Mutual Exclusion Semaphores(互斥信号量)
互斥信号量是一种特殊的二进制信号量,它是针对使用二进制信号量进行互斥 *** 作时存在的一些问题设计的。互斥信号量主要增加了对优先级倒置、删除安全以及递归访问的处理。
状态图:
相关函数:
[c-sharp] view plaincopy
SEM_ID semMCreate
(
int options / mutex semaphore options /
)
区别:
1互斥信号量只能用于互斥 *** 作。
2只能由已经获取了互斥信号量的任务去释放它。
3中断服务程序(ISR)不可以释放(semGive())互斥信号量。
4互斥信号量不支持semFlush() *** 作。
应用方向:
1避免优先级倒置(Priority Inversion):
在上图中,task2等待task1的资源,于是处于Pend状态,这时一个中等优先级的task进来,并抢占了task1的CPU,此时的表现是低优先级task在高优先级的task2前执行。这种现象就是先级倒置。
使用semId = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);就可以避免倒置。
此时,task1的优先级提升与task2一样,至到task2执行完成。
SEM_INVERSION_SAFE不能与SEM_Q_FIFO配对!
2Deletion Safety(安全删除)
使用:semId = semMCreate(SEM_Q_FIFO | SEM_DELETE_SAFE);可以实现安全删除。
其实质是:在Task对互斥信号量执行semTake() *** 作并成功占有该信号量之前,隐含执行了taskSafe() *** 作;在执行semGive() *** 作之后,隐含执行taskUnsafe() *** 作。
如果一个任务task1试图删除一个已经被保护起来的任务task2,task1则将被阻塞起来,直到task2解除保护(释放掉具有删除保护的互斥信号量)才能完成删除工作。
3递归访问
[c-sharp] view plaincopy
InitFun()
{
sem_ID = semMCreate(…);
}
funB()
{
semTake(sem_ID, SEM_FOREVER);
/访问临界资源/
semGive(sem_ID);
}
funA()
{
semTake(sem_ID, SEM_FOREVER);
/访问临界资源/
funB(); //递归访问, 而不会死锁
semGive(sem_ID);
}
五.Counting Semaphores(计数信号量)
计数信号量与二进制信号量都可以用于任务之间的同步与互斥。其不同点在于,计数信号量可记录信号量释放的次数,可以用来监视某一资源的使用状况。
1、信号量的定义:信号量(Semaphore),有时被称为信号灯,是在多钱程环境下使用的一种设施,它负责协调各个线程,以保证它们能够正确、合理的使用公共资源Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。
2、以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。
3、这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
4、抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。
5、在信号量上我们定义两种 *** 作: Wait (等待)和Release (释放)。当一个线程调用Wait *** 作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。
6、Release(释放)实际上是在信号量上执行加 *** 作,对应于车辆离开停车场,该 *** 作之所以叫做“释放”是因为释放了由信号量守护的资源。
7、在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release SemaphoreV分别放置在每个关键代码段的首未端。确认这些信号量VI引用的是初始创建的信号量。
信号量(Semaphore)-- 相当一个信号灯,程序里是一个非负整数,表示状态
可以用来保护两个或多个关键代码段,这些关键代码段不能并发调用。在进入一个关键代码段之前,线程必须获取一个信号量。如果关键代码段中没有任何线程,那么线程会立即进入该框图中的那个部分。一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,然后将Acquire Semaphore 以及Release Semaphore 分别放置在每个关键代码段的首末端。确认这些信号量引用的是初始创建的信号量。
以上就是关于 *** 作oracle数据时报乐观锁异常全部的内容,包括: *** 作oracle数据时报乐观锁异常、如何获得信号量当前值、当用ad转换器获取信号量时候我总是不知道获得的信号量是电压还是电流,这个怎么办我怎么知道获得的是什么等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)