并发线程中的死锁

来源:互联网 发布:酒店英语口语软件下载 编辑:程序博客网 时间: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)…

0 0
原创粉丝点击