找回密码
 立即注册

QQ登录

只需一步,快速开始

tbugs
中级会员   /  发表于:2010-1-8 22:56  /   查看:15373  /  回复:13
“多线程下,多个线程可能会同时使用一个变量,假如这个变量会因为某些需要被任何一个线程修改,那么请问有什么有效的解决方法。”这是我记得去支付宝面试的时候,一个面试官问我的题目,当时我记得答案是加两把锁,一个是X锁,另一个是S锁,呵呵,当时答得很流利,但是今天我在工程上遇到了同样的问题,我却自己很无办法地看着两个可恶的线程毫无配合地使用和修改一个公有变量,原来知识转变到运用上是需要经历的...

后来,在吃晚饭的时候,突然想起X和S锁,果然,工程上的多线程访问共有变量的问题就解决了。今天我在这发表一篇感言,希望大家能够吸取教训,鄙视一个本人...
路,在此绽放。

13 个回复

倒序浏览
Carl
版主   /  发表于:2010-1-11 09:20:00
沙发
顶~
LZ介绍一下X锁和S锁吧,为什么要加两把?各有什么作用?让我们也学习一下~
愿 Engine 归于沉寂,Timer 停止运动,Message Queue 不再流淌,Data Source 为我掌握
回复 使用道具 举报
MushRoom
注册会员   /  发表于:2010-1-11 11:37:00
板凳

回复 2# Carl 的帖子

锁是很可怕的。
据说x锁叫:瞎锁。
s锁叫:死锁。

弄不好要出人命的~
回复 使用道具 举报
winking
葡萄城公司职员   /  发表于:2010-1-11 11:44:00
地板
理论总是和实际有一定的距离~



据说x锁叫:瞎锁。
s锁叫:死锁。

弄不好要出人命的~

不出人命就不会引来无数大虾…………
回复 使用道具 举报
tbugs
中级会员   /  发表于:2010-1-11 12:02:00
5#
哈哈,好,我接下来做个比较详细清晰明了的解析:

首先来解释一下x锁和s锁的定义:

共享锁【S锁】
又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

排他锁【X锁】
又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。

                                                 摘自:《yuwei19840916的专栏-csdn》

为什么非要加2把锁呢?加一把不就够了吗?
释疑:我的确很愚蠢地尝试过去加一把排他X_Lock,就象这样:

  有一个公有变量V=10,线程A与线程B都会用到,它们都允许读取修改V。

好了,现在只有一把锁Lock,专门用于控制V的修改权,假如某个线程要修改V了,就给V加X-Lock(读就不需要了加了),那么其余线程此时不允许对V加X-Lock,也不允许读V。

这些严格的条件看起来貌似万无一失,V应该会被AB两线程很合理的利用吧,但是噩梦开始了~

假如 x-lock此时为false,只有A线程正在准备读V的值,但不修改,情景就像如下这样。
A线程:
while(X-lock)// X-lock为false
{
wait(10);
}
--------------markA------------
read(V);
正如以上代码所说,A线程正检测V是否被加X-Lock,一旦发现x-lock为false,就进行下一步read(V),呵呵,假如x-lock一直为假(这时期B线程一直不去修改V),那么A线程读到的值当然是正确的了。但是在markA位置,假如A线程时间片到了,并且B线程突然要修改V,给V++,B线程此时加x-lock锁,不过,呵呵A线程已经跳过了检测锁的代码了,B线程如何加锁都不管用了,就像这样:

v=10;
-----A线程-----
while(X-lock)// X-lock为false
{
wait(10);
}
-----B线程-----
x-lock=true;
v++;//v=11;
-----A线程-----
read(v);//read v=11
******exception thrown: A should read v=10,rather than 11******
-----B线程-----
x-lock=false;

故事就是这样了,A线程在不知情的情况下读到了v=11,而事实上,A需要读V=10才符合要求。这就是加一把锁的问题所在了。毕竟因为两个不同的线程,你永远不知道什么时候AB线程在哪个地方切换!就算是v=v+1的一句很普通的话,一个线程都有可能分两次走,因为机器执行的是机械代码,而不是高级语言,v=v+1会变转变为汇编语言,
  mov  al,A ;A→al
   add  al,1 ;A+1→al
这样线程很可能在第一句执行完交出执行权给别的线程,别的线程可能在此时改掉A的值,导致悲剧的发生。

嗯,估计大家现在知道发生什么事了吧,加一把锁的问题在于,检测锁的代码有时候不起作用~~

那为什么两把锁就能够解决上面的问题呢?
呵呵,继续看下去,你就知道了~~
现在同样的情况,只是多了一把s-lock,凡是要读v值的线程,先给他加s锁,读完之后在解锁

v=10;
-----A线程-----
while(X-lock)// X-lock此时为false
{
wait(10);
}//保证x-lock为假,才能给s-lock=true;
s-lock=true;//*********注意,A线程在<"想">读V值时,加s锁啦
while(X-lock)// 再次确定x-lock为假,X-lock此时为false
{
wait(10);
}
-----B线程-----
while(s-lock==true)//b线程要加X锁时,必须要保证s锁为假
{
wait(10);
}
//B线程等不到s-lock为假就被迫交出执行权了,接着到A执行
---------A线程--------
read(v);//read v=10
s-lock=false;//A线程读完了,解开s-lock
---------B线程----------
while(s-lock==true)//b线程要加X锁时,必须要保证s锁为假
{
wait(10);
}
x-lock=true;
v++;//v=11;
-----A线程-----
//no mission
-----B线程-----
x-lock=false;


好了,问题貌似真的解决了,其实加两把锁比加一把实际,是因为,两把锁能够锁得更“牢固”些!!肯定比一把锁好嘛。
可能以上例子有bug,请读者认真研读指正(我强烈的感觉,这个例子有问题,请指正问题)。
路,在此绽放。
回复 使用道具 举报
tbugs
中级会员   /  发表于:2010-1-11 12:09:00
6#

回复 3# MushRoom 的帖子

,很形象~~~
路,在此绽放。
回复 使用道具 举报
Carl
版主   /  发表于:2010-1-11 12:25:00
7#
学习了
愿 Engine 归于沉寂,Timer 停止运动,Message Queue 不再流淌,Data Source 为我掌握
回复 使用道具 举报
alenyin
葡萄城公司职员   /  发表于:2010-1-13 14:17:00
8#
学习了。。。
回复 使用道具 举报
MushRoom
注册会员   /  发表于:2010-1-13 16:33:00
9#
是因为,两把锁能够锁得更“牢固”些!!肯定比一把锁好嘛。


某地有一人一个月内连丢了三辆自行车,于是在新车上锁了三把锁,后座上挂一纸牌写着:叫你偷!第二天一早下楼,车果然还在,只是车上又多了三把锁,纸牌的反面写道:叫你锁!
回复 使用道具 举报
ono
论坛元老   /  发表于:2010-1-13 17:21:00
10#
学习了..
回复 使用道具 举报
12下一页
您需要登录后才可以回帖 登录 | 立即注册
返回顶部