并发线程中的死锁
来源:互联网 发布:酒店英语口语软件下载 编辑:程序博客网 时间:2024/05/17 09:15
多线程中不可避免的要对共享数据共享类等进行操作,但为了让同一时间只有一个线程访问该代码块,引入了锁的概念。
C# 中锁的原型是这样的
lock (x){ DoSomething();}
首先 为什么上面这段话能够锁定代码?最关键的就是这个X对象。事实上X 是任意一种引用类型。它在这起的作用就是任何线程执行到lock(x)的时候,X需要独享才能运行下面的代码,若假定现在有3个线程A,B,C都执行到了lock(X)而ABC因为此时都占有X,这时ABC就要停下来排个队, 一个一个使用X,从而起到在下面的代码块内只有一个线程在运行(因为此时只有一个线程独享X,其余两个在排队),所以这个X必须是所有要执行临界区域代码 进程必须共有的一个资源,从而起到抑制线程的作用。
说到锁 ,接下来我们还有必要谈谈死锁。
我们都知道产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
死锁程序出现的一种假死现象。多线程争抢同一把锁,造成的程序假死现象。死锁现象出现在同步代码块的嵌套形式。其实我们还可以用一张图来表示。
当前有左L右R两个线程,在每隔线程里都有两个嵌套形式。
左边外层我们用锁A 里层用锁B .右边外层用锁B 里层用锁A
这样一来,我们可以看到左右两个小人分别拿到AB锁进入左右两个程序 外层后,再进入里层程序的时候 左边的需要B钥匙但是他没有,只能等待。右边的需要A钥匙,但是他也没有,也只能等待。如此,悲剧发生了。。
对应上面的图,简单写了一个这样的程序
public class DeadLock { private bool flag; static object lockKeyA = new object(); static object lockKeyB = new object(); //定义一个标记,标记线程A还是B先进 public DeadLock(bool flag) { this.flag = flag; } public void run() { while (true) { //判断变量的值是true 进入A门 否则B门 if (flag) { lock (lockKeyA) { Console.WriteLine("A门在外:当前已经进入A门等待B门钥匙"); lock (lockKeyB) { Console.WriteLine("A门在外:当前已经进入A门并进入B门"); } } } else { lock (lockKeyB) { Console.WriteLine("B门在外:当前已经进入B门等待A门钥匙"); lock (lockKeyA) { Console.WriteLine("B门在外:当前已经进入B门并进入A门"); } } } } } }
调用时:
private void Form1_Load(object sender, EventArgs e) { DeadLock dealLockA = new DeadLock(true); DeadLock dealLockB = new DeadLock(false); Thread t1 = new Thread(dealLockA.run); Thread t2 = new Thread(dealLockB.run); t1.Start(); t2.Start(); }
我们来看一下效果
可以看到虽然我的程序里的控制条件是while(true) 但是在程序运行的过一段时间后它已经自己锁死停止了。这一时刻,我们看到A门在外的那个线程 当前已经进入A门等待B门钥匙。B门在外的那个线程,当前已经进入B门等待A门钥匙。程序假死了。。以上完全符合我们在上图中进行的分析。
此外,对上面的内容, 还有一点要注意。我们说锁的lock(X)中X是一种对象引用类型。
也就是说假如我定义了两个锁,String strLockA=”123” String strLockB=”123” 他们都是指向同一个对象,那么这两把锁是一个。
我做了一个这样的例子。类LockClass1和LockClass2中分别定义了两个私有的锁。仍然是嵌套锁。
类LockClass1:
/// <summary> /// <summary> /// 锁是引用类型验证类1-马丹妹-2015年11月30日 /// </summary> /// </summary> public class LockClass1 { String strLock1 = "123"; String strLock2 = "456"; public void TestLock() { while (true) { lock (strLock1) { Console.WriteLine("LockClass1 中执行到strLock1"); lock (strLock2) { Console.WriteLine("LockClass1 中执行到strLock2"); } } } } }
类LockClass2
/// <summary> /// 锁是引用类型验证类2-马丹妹-2015年11月30日 /// </summary> public class LockClass2 { String strLock3 = "123"; String strLock4 = "456"; public void TestLock() { while (true) { lock (strLock4) { Console.WriteLine("LockClass2 中执行到strLock4"); lock (strLock3) { Console.WriteLine("LockClass2 中执行到strLock3"); } } } } }
调用时:
private void Form1_Load(object sender, EventArgs e) { LockClass1 lockClass1 = new LockClass1(); LockClass2 lockClass2 = new LockClass2(); Thread thread1 = new Thread(lockClass1.TestLock); Thread thread2 = new Thread(lockClass2.TestLock); thread1.Start(); thread2.Start(); }
按我们往常的理解,LockClass1 和LockClass2中的类是不会相互干扰的,因为他们没有公共数据,而且锁也是私有的。意味着另一个类根本无法使用到另一个类里的锁。
但是我运行了两次结果是:
第一次 程序刚运行就 不动了:
第二次 程序运行了两三秒就不动了:
对于并发线程,这一点很好解释。因为两个线程的执行顺序是随机的,所以当两个线程恰好到达某一个状态时的时间也会是随机的。这就是为什么很多程序的死锁潜伏期很长,甚至在程序正常的开发中都没有发生过,结果项目一上线,程序死锁了。这时候,项目经理的表情一定是笑(xiang)呵(sha)呵(si)哒(ni)…
- 并发线程中的死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- Java并发编程:线程死锁
- 线程中的死锁问题
- 线程中的死锁
- 150天成为高级程序员之路
- nmake下一些错误的解决办法
- memcached的安装及使用
- uva524
- IOS实现视频直播-RTMP协议的一些参考资料
- 并发线程中的死锁
- iOS 设计模式——组合
- 【HTTP】Fiddler(二) - 使用Fiddler做抓包分析
- 数据结构实践项目——查找(二)
- SAP物料帐下修改物料的价格
- Android 中 EventBus 的使用(1):为什么要使用 EventBus
- spring源码剖析(三)自定义标签实现及使用
- 使用Matlab进行特征选择
- COM教程(0-序章)