zoukankan      html  css  js  c++  java
  • [转载]: C#线程同步与死锁

    本文继续C#线程系列讲座之四,C#线程同步与死锁。

    AD:


      在上一讲介绍了使用lock来实现C#线程同步。实际上,这个lock是C#的一个障眼法,在C#编译器编译lock语句时,将其编译成了调用Monitor类。先看看下面的C#源代码:

      1. public static void MyLock()
      2. {
      3. lock (typeof(Program))
      4. {
      5. }
      6. }

      上面的代码通过lock语句使MyLock同步,这个方法被编译成IL后,代码如图1所示。


      代码如图1


      图1


      从上图被标注的区域可以看到,一条lock语句被编译成了调用Monitor的Enter和Exit方法。Monitor在System.Threading命名空间中。lock的功能就相当于直接调用Monitor的Entry方法,所不同的是,lock方法在结束后,会自动解除锁定,当然,在IL中是调用了Monitor的Exit方法,但在C#程序中,看起来是自动解锁的,这类似于C#中的using语句,可以自动释放数据库等的资源。但如果直接在C#源程序中使用Monitor类,就必须调用Exit方法来显式地解除锁定。如下面的代码所示:

      1. Monitor.Entry(lockObj);
      2. try
      3. {
      4. // lockObj的同布区
      5. }
      6. catch(Exception e)
      7. {
      8. // 异常处理代码
      9. }
      10. finally
      11. {
      12. Monitor.Exit(lockObj); // 解除锁定
      13. }

      Exit方法最后在finally里调用,这样无论在方法在发生异常、返回还是正常执行,都会执行到finally,并调用Exit方法解除锁定。


      Monitor类不仅可以完全取代lock语句(如果只使用lock语句本身的功能,最好还是直接用lock语句吧),还可以使用TryEntry方法设置一个锁定超时,单位是毫秒。如下面的代码所示:

      1. if(Monitor.TryEntry(lockObj, 1000))
      2. {
      3. try
      4. {
      5. }
      6. finally
      7. {
      8. Monitor.Exit(lockObj);
      9. }
      10. }
      11. else
      12. {
      13. // 超时后的处理代码
      14. }

      上面的代码设置了锁定超时时间为1秒,也就是说,在1秒中后,lockObj还未被解锁,TryEntry方法就会返回false,如果在1秒之内,lockObj被解锁,TryEntry返回true。我们可以使用这种方法来避免死锁,如下面的代码所示:

      1. class Program
      2. {
      3. private static Object objA = new Object();
      4. private static Object objB = new Object();
      5. public static void LockA()
      6. {
      7. if (Monitor.TryEnter(objA, 1000))
      8. {
      9. Thread.Sleep(1000);
      10. if (Monitor.TryEnter(objB, 2000))
      11. {
      12. Monitor.Exit(objB);
      13. }
      14. else
      15. {
      16. Console.WriteLine("LockB timeout");
      17. }
      18. Monitor.Exit(objA);
      19. }
      20. Console.WriteLine("LockA");
      21. }
      22. public static void LockB()
      23. {
      24. if (Monitor.TryEnter(objB, 2000))
      25. {
      26. Thread.Sleep(2000);
      27. if (Monitor.TryEnter(objA, 1000))
      28. {
      29. Monitor.Exit(objA);
      30. }
      31. else
      32. {
      33. Console.WriteLine("LockA timeout");
      34. }
      35. Monitor.Exit(objB);
      36. }
      37. Console.WriteLine("LockB");
      38. }
      39. public static void Main()
      40. {
      41. Thread threadA = new Thread(LockA);
      42. Thread threadB = new Thread(LockB);
      43. threadA.Start();
      44. threadB.Start();
      45. Thread.Sleep(4000);
      46. Console.WriteLine("线程结束");
      47. }
      48. }

      上面的代码是在上一讲举的死锁的例子,但在这一讲将lock语句改成了TryEntry方法,而且设置了锁定超时间,由于在等待一定时间后,不管被锁定的对象是否被解锁,TryEntry方法都会返回,因此,上面的代码是不会死锁的。运行上面的代码的结果如图2所示。


      代码的结果如图2


      图2


      如果TryEntry方法的超时时间为System.Threading.Timeout.Infinite,TryEntry方法就相当于Entry方法,如果超时时间为0,不管是否解锁,TryEntry方法都会立即返回。


      这样就解决了C#线程同步与死锁的问题。

      做个快乐的自己。
    • 相关阅读:
      【基础算法】- 全排列
      【基础算法】- 2分查找
      区块链培训
      Static Binding (Early Binding) vs Dynamic Binding (Late Binding)
      test
      No data is deployed on the contract address!
      "throw" is deprecated in favour of "revert()", "require()" and "assert()".
      Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning.
      京都行
      Failed to write genesis block: database already contains an incompatible
    • 原文地址:https://www.cnblogs.com/Jessy/p/2169923.html
    Copyright © 2011-2022 走看看