【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)

时间:2022-12-27 09:34:43

本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过 本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释。让我们来看看这几个类的关系图:

【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)

1.lock关键字

lock是C#关键词,它将语句块标记为临界区,确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。方法是获取给定对象的互斥锁,执行语句,然后释放该锁。

MSDN上给出了使用lock时的注意事项通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则。

1)如果实例可以被公共访问,将出现 lock (this) 问题。

2)如果 MyType 可以被公共访问,将出现 lock (typeof (MyType))
问题由于一个类的所有实例都只有一个类型对象(该对象是typeof的返回结果),锁定它,就锁定了该对象的所有实例。微软现在建议不要使用
lock(typeof(MyType)),因为锁定类型对象是个很缓慢的过程,并且类中的其他线程、甚至在同一个应用程序域中运行的其他程序都可以访问
该类型对象,因此,它们就有可能代替您锁定类型对象,完全阻止您的执行,从而导致你自己的代码的挂起。

3)由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。这个问题和.NET Framework创建字符串的机制有关系,如果两个string变量值都是"myLock",在内存中会指向同一字符串对象。

最佳做法是定义 private 对象来锁定, 或 private static对象变量来保护所有实例所共有的数据。

我们再来看看lock关键字的本质,lock关键字其实就是对Monitor类的Enter()和Exit()方法的封装,并通过
try...catch...finally语句块确保在lock语句块结束后执行Monitor.Exit()方法,释放互斥锁。

2.Monitor类

Monitor类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问临界区的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该
锁。还可以使用 Monitor
来确保不会允许其他任何线程访问正在由锁的所有者执行的应用程序代码节,除非另一个线程正在使用其他的锁定对象执行该代码。

通过对lock关键字的分析我们知道,lock就是对Monitor的Enter和Exit的一个封装,而且使用起来更简洁,因此Monitor类的Enter()和Exit()方法的组合使用可以用lock关键字替代。

另外Monitor类还有几个常用的方法:

TryEnter()能够有效的解决长期死等的问题,如果在一个并发经常发生,而且持续时间长的环境中使用TryEnter,可以有效防止死锁或者长时间
的等待。比如我们可以设置一个等待时间bool gotLock =
Monitor.TryEnter(myobject,1000),让当前线程在等待1000秒后根据返回的bool值来决定是否继续下面的操作。

Wait()释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。

Pulse(),PulseAll()向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被
放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。

注意:Pulse、PulseAll和Wait方法必须从同步的代码块内调用。

我们假定一种情景:妈妈做蛋糕,小孩有点馋,妈妈每做好一块就要吃掉,妈妈做好一块后,告诉小孩蛋糕已经做好了。下面的例子用Monitor类的Wait和Pulse方法模拟小孩吃蛋糕的情景。

  1. //仅仅是说明Wait和Pulse/PulseAll的例子
  2. //逻辑上并不严密,使用场景也并不一定合适
  3. class MonitorSample
  4. {
  5. private int n = 1;  //生产者和消费者共同处理的数据
  6. private int max = 10000;
  7. private object monitor = new object();
  8. public void Produce()
  9. {
  10. lock (monitor)
  11. {
  12. for (; n <= max; n++)
  13. {
  14. Console.WriteLine("妈妈:第" + n.ToString() + "块蛋糕做好了");
  15. //Pulse方法不用调用是因为另一个线程中用的是Wait(object,int)方法
  16. //该方法使被阻止线程进入了同步对象的就绪队列
  17. //是否需要脉冲激活是Wait方法一个参数和两个参数的重要区别
  18. //Monitor.Pulse(monitor);
  19. //调用Wait方法释放对象上的锁并阻止该线程(线程状态为WaitSleepJoin)
  20. //该线程进入到同步对象的等待队列,直到其它线程调用Pulse使该线程进入到就绪队列中
  21. //线程进入到就绪队列中才有条件争夺同步对象的所有权
  22. //如果没有其它线程调用Pulse/PulseAll方法,该线程不可能被执行
  23. Monitor.Wait(monitor);
  24. }
  25. }
  26. }
  27. public void Consume()
  28. {
  29. lock (monitor)
  30. {
  31. while (true)
  32. {
  33. //通知等待队列中的线程锁定对象状态的更改,但不会释放锁
  34. //接收到Pulse脉冲后,线程从同步对象的等待队列移动到就绪队列中
  35. //注意:最终能获得锁的线程并不一定是得到Pulse脉冲的线程
  36. Monitor.Pulse(monitor);
  37. //释放对象上的锁并阻止当前线程,直到它重新获取该锁
  38. //如果指定的超时间隔已过,则线程进入就绪队列
  39. Monitor.Wait(monitor,1000);
  40. Console.WriteLine("孩子:开始吃第" + n.ToString() + "块蛋糕");
  41. }
  42. }
  43. }
  44. static void Main(string[] args)
  45. {
  46. MonitorSample obj = new MonitorSample();
  47. Thread tProduce = new Thread(new ThreadStart(obj.Produce));
  48. Thread tConsume = new Thread(new ThreadStart(obj.Consume));
  49. //Start threads.
  50. tProduce.Start();
  51. tConsume.Start();
  52. Console.ReadLine();
  53. }
  54. }

这个例子的目的是要理解Wait和Pulse如何保证线程同步的,同时要注意Wait(obeject)和Wait(object,int)方法
的区别,理解它们的区别很关键的一点是要理解同步的对象包含若干引用,其中包括对当前拥有锁的线程的引用、对就绪队列(包含准备获取锁的线程)的引用和对
等待队列(包含等待对象状态更改通知的线程)的引用。

【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)的更多相关文章

  1. C&num;多线程:深入了解线程同步lock&comma;Monitor&comma;Mutex&comma;同步事件和等待句柄(中)

    本篇继续介绍WaitHandler类及其子类 Mutex,ManualResetEvent,AutoResetEvent的用法..NET中线程同步的方式多的让人看了眼花缭乱,究竟该怎么去理解呢?其实, ...

  2. C&num; 多线程&lpar;lock&comma;Monitor&comma;Mutex&comma;同步事件和等待句柄&rpar;

    本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而 ...

  3. 重新想象 Windows 8 Store Apps &lpar;46&rpar; - 多线程之线程同步&colon; Lock&comma; Monitor&comma; Interlocked&comma; Mutex&comma; ReaderWriterLock

    [源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...

  4. C&num;多线程同步事件及等待句柄AutoResetEvent 和 ManualResetEvent

    最近捣鼓了一下多线程的同步问题,发现其实C#关于多线程同步事件处理还是很灵活,这里主要写一下,自己测试的一些代码,涉及到了AutoResetEvent 和 ManualResetEvent,当然还有也 ...

  5. C&num;各种同步方法 lock&comma; Monitor&comma;Mutex&comma; Semaphore&comma; Interlocked&comma; ReaderWriterLock&comma;AutoResetEvent&comma; ManualResetEvent

    看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...

  6. C&num;多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  7. python类库32&lbrack;多进程同步Lock&plus;Semaphore&plus;Event&rsqb;

    python类库32[多进程同步Lock+Semaphore+Event]   同步的方法基本与多线程相同. 1) Lock 当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突. imp ...

  8. C&num;使用Monitor类、Lock和Mutex类进行多线程同步

    在多线程中,为了使数据保持一致性必须要对数据或是访问数据的函数加锁,在数据库中这是很常见的,但是在程序中由于大部分都是单线程的程序,所以没有加锁的必要,但是在多线程中,为了保持数据的同步,一定要加锁, ...

  9. &period;NET多线程之线程安全,Lock(锁)、Monitor(同步访问)、LazyInitializer(延迟初始化)、Interlocked(原子操作)、static(静态)构造函数、volatile、

    1.什么是线程安全 线程安全是编程中的术语,指某个函数.函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成.一般来说,线程安全的函数应该为每个调用它的线程分配专门的 ...

随机推荐

  1. openstack虚拟机启动过程

    核心项目3个 1.控制台 服务名:Dashboard 项目名:Horizon 功能:web方式管理云平台,建云主机,分配网络,配安全组,加云盘 2.计算 服务名:计算 项目名:Nova 功能:负责响应 ...

  2. Codeforces Round &num;337 Vika and Segments

    D. Vika and Segments time limit per test:  2 seconds     memory limit per test:  256 megabytes input ...

  3. python 数据类型之数float

    1.float 对象有一个重要的方法is_intger.如果这个float对应在转化为int时不会有精度的丢失就返回True,不然就返回False #!/usr/bin/python #!coding ...

  4. Java连接Azure SQL Database

    Azure SQL Database是Azure上的数据库PAAS服务,让用户可以快速的创建和使用SQL数据库而不用担心底层的备份,安全,运维,恢复等繁琐的工作,本文简单介绍如何使用Java程序连接到 ...

  5. IIS7禁止后台访问

    设置只能内网访问 1.添加允许内网访问规则 2.编辑功能设置

  6. 【1414软工助教】团队作业8——第二次项目冲刺(Beta阶段) 得分榜

    题目 团队作业8--第二次项目冲刺(Beta阶段) 往期成绩 个人作业1:四则运算控制台 结对项目1:GUI 个人作业2:案例分析 结对项目2:单元测试 团队作业1:团队展示 团队作业2:需求分析&a ...

  7. 解决Oracle登录时出现无法处理服务名问题

    1.首先找到客户端的tnsnames.ora文件,打开看看里面有没有配置相应的服务器名,服务器名就是你的数据库名: 2.如果有相应的服务器名,那就检查一下配置信息是否错误,如果没有就添加: 3.配置信 ...

  8. 在Python中使用Redis

    在Python中要使用Redis数据库,首先要安装redis 之前的博客中有写到在命令行模式下操作Redis数据库. 要在项目中使用的话可以这么做: 通过初始化 redis.Redis,得到返回的对象 ...

  9. 谈谈Python中的decorator装饰器,如何更优雅的重用代码

    众所周知,Python本身有很多优雅的语法,让你能用一行代码写出其他语言很多行代码才能做的事情,比如: 最常用的迭代(eg: for i in range(1,10)), 列表生成式(eg: [ x* ...

  10. C&plus;&plus;中public&sol;protect&sol;private三种访问权限控制

    一.成员访问权限控制 1.public (1)public成员变量可以被成员函数访问  [访问性] (2)public成员可以被实体对象访问  [访问性] (3)public成员可以成为子类成员  [ ...