• 《C#多线程编程实战》1.11 Monitor.TryEnter()避免死锁


    这章的内容是真的有意思

    特别是代码。

    先贴上代码:

    class Program
        {
            static void Main(string[] args)
            {
                object lock1 = new object();
    
                object lock2 = new object();
               
                new Thread(() => LockTooMuch(lock1, lock2)).Start();
                        
                lock(lock2)
                {
                    Thread.Sleep(1000);
    
                    Console.WriteLine("Monitor.TryEnter allow not to get stuck,returning false after a specified timer is elapsed");
    
                   if(Monitor.TryEnter(lock1,5000))
                    {
                        Console.WriteLine("Acquired a protected resource succesfully");
    
                    }
                     else
                    {
                        Console.WriteLine("Timerout cquiring a resource");
                      
                    }
                }
    
    
                new Thread(() => LockTooMuch(lock1, lock2)).Start();
              
                lock (lock2)
                {
                    Console.WriteLine("This will be deadlock");
                    Thread.Sleep(1000);
                    lock (lock1)
                    {
                        Console.WriteLine("Acquired a protected resource succesfully");
                    }
    
                }
                Console.ReadKey();
            }
            static  void LockTooMuch(object Lock1,object  Lock2)
            {
                lock(Lock1)
                {
                    Thread.Sleep(1000);
               
                    lock (Lock2)
                    {
    
                    }
                  
                }
            }
        }

    说实话,这章代码,我一开始没看明白。真的。

    有些时候真的就是难者不会会者不难。

    咱慢慢来来说。

    咱先从这一部分开始:

     1   static  void LockTooMuch(object Lock1,object  Lock2)
     2         {
     3             lock(Lock1)
     4             {
     5                 Thread.Sleep(1000);
     6            
     7                 lock (Lock2)
     8                 {
     9 
    10                 }
    11               
    12             }
    13         }

    这一部分的代码很好理解:

    传入两个引用类型的object参数。

    然后是代码主要部分先是

    锁死lock1

    然后再lock1里面锁死lock2.

    这个部分的锁死的代码,也就意味着解锁的顺序,lock1等待lock2执行完毕才会解锁lock1.

    本章说避免锁死。那么什么是锁死?

    那么我们来原书的代码:

        new Thread(() => LockTooMuch(lock1, lock2)).Start();          
                lock (lock2)
                {
                    Console.WriteLine("This will be deadlock");
                    Thread.Sleep(1000);
                    lock (lock1)
                    {
                        Console.WriteLine("Acquired a protected resource succesfully");
                    }
                }

    这一部分的代码加上静态的LockTooMatch的方法运行就会锁死。

    我们按照这个思路清理一下代码:

     static void Main(string[] args)
            {
                object lock1 = new object();
    
                object lock2 = new object();
               
                                   
                new Thread(() => LockTooMuch(lock1, lock2)).Start();
                
                lock (lock2)
                {
                    Console.WriteLine("This will be deadlock");
                    Thread.Sleep(1000);
                    lock (lock1)
                    {
                        Console.WriteLine("Acquired a protected resource succesfully");
                    }
    
                }
                Console.ReadKey();
            }
            static  void LockTooMuch(object Lock1,object  Lock2)
            {
                lock(Lock1)
                {
                    Thread.Sleep(0000);
               
                    lock (Lock2)
                    {
    
                    }
                  
                }
            }

     see

    永远锁死

     为什么只会执行到这里呢?

      Console.WriteLine("This will be deadlock");

    一步一步的

    首先程序新开线程运行LockTooMuch,然后再主线程锁LOCK2输出内容,线程等待1S之后再锁LOCK1 在输出语句。

     1             lock (lock2)
     2             {
     3                 Console.WriteLine("This will be deadlock");
     4                 Thread.Sleep(1000);
     5                 lock (lock1)
     6                 {
     7                     Console.WriteLine("Acquired a protected resource succesfully");
     8                 }
     9 
    10             }

    按照我们之前说的东西,L2必须等待L1结束才会释放。

    这是主线程的部分。

    下面是方法的部分

     static  void LockTooMuch(object Lock1,object  Lock2)
            {
                lock(Lock1)
                {
                    Thread.Sleep(1000);
               
                    lock (Lock2)
                    {
    
                    }
                  
                }
            }

    同样也是L1等待L2的结束才会是否。

    主线程和新开线程是同时的。

    也就是会有竞争。

    那么执行的顺序是什么呢?

    很简单

    主线程新开线程

    主线锁LOCK2

    新开线程执行锁LOCK1

    主线程输出

    新开线程等待1s

    主线程等待1s

    新开线程锁LOCK2但是失败

    主线程锁LOCK1但是失败

    程序停止继续向下运行

    为什么主线程,新线程锁LOCK为什么会失败呢?

    新开线程锁lock2会失败是因为主线程在用lock2而且还没有用完。

    主线程锁lock1会失败是应为新开线程做用lock1还么有用完。

    所以会锁死。

     ok 我们知道锁死的原因,下面我们通过Monitor.TryEnter来避免锁死。

    Monitor.TryEnter是作用什么呢?

    TryEnter有三个重载方法。

    常用的是带有等待时间的。

    作用就是在规定的时间去尝试解锁资源,或者获取资源。如果获取成功就返回true,不成功就返回false。这个代码总会返回结果。超时也会返回不过是false,也就说锁死了也会有办法来处理,不会尴尬的停在那里了

    我们看看代码:

     new Thread(() => LockTooMuch(lock1, lock2)).Start();
                        
                lock(lock2)
                {
                    Thread.Sleep(1000);
    
                    Console.WriteLine("Monitor.TryEnter allow not to get stuck,returning false after a specified timer is elapsed");
    
                   if(Monitor.TryEnter(lock1,5000))
                   {
                        Console.WriteLine("Acquired a protected resource succesfully");
    
                   }
                    else
                   {
                        Console.WriteLine("Timerout cquiring a resource");
                      
                   }
                }

    ok 清理下会锁死的代码,来运行代码

    执行了else内的代码,说明尝试解锁lock1失败了,和上面那个会死锁的代码一样的结果,但是这个却可以继续持续其他代码,不会卡在死锁的部分。

    我们来看看执行的过程【大家可以在VS中 使用断点,逐语句等方式来观察线程的运行顺序】

    主线程新开线程

    主线锁LOCK2

    新开线程执行锁LOCK1

    主线程等待1s

    新开线程等待1s

    主线程输出语句

    新开线程锁LOCK2但是失败

    主线程尝试解锁LOCK1,5s内。(成功返回true 失败返回fasle 超时返回false)但是失败

    主线程输出else内容

    程序结束

    执行的顺序和死锁的顺序差不多,但是结果却不一样的因为Monitor.TryEnter避免的死锁的尴尬地步,程序多线程中出现死锁,使用monitor.tryenter会很好的避免死锁。

  • 相关阅读:
    有赞 MySQL 自动化运维之路 — ZanDB
    程序员你为什么这么累【续】:编码习惯之日志建议
    Optimizing web servers for high throughput and low latency
    难道他们说的都是真的?
    Netty 长连接服务
    python opencv 读取图片 返回图片某像素点的b,g,r值
    Python下的图像处理库,你选哪个?
    python中numpy.savetxt 参数
    Python如何安装OpenCV库
    Python图像处理库PIL的基本概念介绍
  • 原文地址:https://www.cnblogs.com/T-ARF/p/9276549.html
Copyright © 2020-2023  润新知