在上一讲介绍了使用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

从上图被标注的区域可以看到,一条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

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

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

C#线程同步与死锁Monitor的更多相关文章

  1. C#:多线程、线程同步与死锁

    推荐阅读: C#线程系列讲座(1):BeginInvoke和EndInvoke方法 C#线程系列讲座(2):Thread类的应用 C#线程系列讲座(3):线程池和文件下载服务器 C#线程系列讲座(4) ...

  2. 线程同步 – lock和Monitor

    在多线程代码中,多个线程可能会访问一些公共的资源(变量.方法逻辑等等),这些公共资源称为临界区(共享区):临界区的资源是不安全,所以需要通过线程同步对多个访问临界区的线程进行控制. 同样,有些时候我们 ...

  3. Java之线程,常用方法,线程同步,死锁

    1, 线程的概念 进程与线程 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据 ...

  4. java 线程同步、死锁

    转载地址:速学堂 https://www.sxt.cn/Java_jQuery_in_action/eleven-thread-synchronization.html 什么是线程同步  同步问题的提 ...

  5. java ->多线程_线程同步、死锁、等待唤醒机制

    线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. l  我们通过一个案例,演示线 ...

  6. Java线程同步与死锁认识

    讲下自己的认识,算小小的总结吧! synchroized 具有同步线程的功能,它的处理机制类似于给参数里面的对象赋一个标记值,来表明当前状态,当程序里面某个线程执行synchroized里面的代码段时 ...

  7. C# 线程同步之排它锁/Monitor监视器类

    一.Monitor类说明,提供同步访问对象的机制. 1.位于System.Threading命名空间下,mscorlib.dll程序集中. 2.Monitor通过获取和释放排它锁的方式实现多线程的同步 ...

  8. 线程同步、死锁和通信——Java多线程(二)

    一.多线程同步 上一篇随笔中,我曾遇到对多线程程序的多次运行结果不一致的情况,这主要是因为没有对这些线程在访问临界资源做必要的控制,而接下来就用线程的同步来解决这个问题. 1.同步代码块 class ...

  9. 四十四、Linux 线程——线程同步之死锁以及线程和信号

    44.1 死锁 死锁: 两个线程试图同时占有两个资源,并按不同的次序锁定相应的共享资源 解决方式: 按相同的次序锁定相应的共享资源 使用函数 pthread_mutex_trylock(),它是函数 ...

随机推荐

  1. ref:关于JAVA中一些安全漏洞示例说明及如何规避方法代码示例总结分享

    ref:http://www.xwood.net/_site_domain_/_root/5870/5874/t_c268166.html 标签:安全,漏洞,健壮,java,SQL注入,SS及CSRF ...

  2. Entity Framework Core介绍(1)

    介绍 Entity Framework (EF) Core 是轻量化.可扩展和跨平台版的常用 Entity Framework 数据访问技术. EF Core 可用作对象关系映射程序 (O/RM),以 ...

  3. NetCore+Dapper WebApi架构搭建(二):底层封装

    看下我们上一节搭建的架构,现在开始从事底层的封装 1.首先需要一个实体的接口IEntity namespace Dinner.Dapper { public interface IEntity< ...

  4. 理解URI

    ---恢复内容开始--- 参考 https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E6%A0%87%E5%BF%97 ...

  5. 1go基本语法

    // week1 project main.go//导入一个包(目录) package main //导入方法 import "fmt" //createMessage函数(参数, ...

  6. 【WIN10】程序內文件讀取與保存

    DEMO下載:http://yunpan.cn/cFHIZNmAy4ZtH  访问密码 cf79 1.讀取與保存文件 Assets一般被認為是保存用戶文件數據的地方.同時,微軟還支持用戶自己創建文件夾 ...

  7. codevs 2577 医院设置

    2577 医院设置 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold   题目描述 Description 设有一棵二叉树,如下图 其中,圈中数字表示结点居民的人口.圈边 ...

  8. Codeforces Round #489 (Div. 2)

    A. Nastya and an Array time limit per test 1 second memory limit per test 256 megabytes input standa ...

  9. Java NIo 笔记001

    1. Channel Channel接口只提供了两个方法: package java.nio.channels; public interface Channel { public boolean i ...

  10. Oil Deposits 搜索 bfs 强联通

    Description The GeoSurvComp geologic survey company is responsible for detecting underground oil dep ...