Monitor的code如下,非常简单:

public static class Monitor
{public static extern void Enter(Object obj);public static void Enter(Object obj, ref bool lockTaken){if (lockTaken)ThrowLockTakenException();ReliableEnter(obj, ref lockTaken);Contract.Assert(lockTaken);}private static extern void ReliableEnter(Object obj, ref bool lockTaken);public static void TryEnter(Object obj, ref bool lockTaken){if (lockTaken)ThrowLockTakenException();ReliableEnterTimeout(obj, 0, ref lockTaken);}private static extern void ReliableEnterTimeout(Object obj, int timeout, ref bool lockTaken);public static extern void Exit(Object obj);public static bool Wait(Object obj, int millisecondsTimeout, bool exitContext){if (obj == null)throw (new ArgumentNullException("obj"));return ObjWait(exitContext, millisecondsTimeout, obj);}private static extern bool ObjWait(bool exitContext, int millisecondsTimeout, Object obj);public static void Pulse(Object obj){if (obj == null){throw new ArgumentNullException("obj");}Contract.EndContractBlock();ObjPulse(obj);}private static extern void ObjPulse(Object obj);public static void PulseAll(Object obj){if (obj == null){throw new ArgumentNullException("obj");}Contract.EndContractBlock();ObjPulseAll(obj);}private static extern void ObjPulseAll(Object obj);public static bool IsEntered(object obj){if (obj == null)throw new ArgumentNullException("obj");return IsEnteredNative(obj);}private static extern bool IsEnteredNative(Object obj);
}

核心方法就是Enter和Exit,其中lock关键字就是这2个方法的一个封装,剩下的Wait、Pulse和PulseAll也是很重要的方法,但是平时运用的比较少。所以这里重点说说Wait、Pulse和PulseAll方法。

线程优先顺序: 【等待队列】->【就绪队列】->【拥有锁线程】这个是重点,下文多次会提到,其中的微妙关系的核心也来源于这个执行顺序。
MSDN官方备注:同步的对象包含若干引用,其中包括对当前拥有锁的线程的引用、对就绪队列的引用和对等待队列的引用。我的提醒:竞争对象锁的线程都是处于就绪队列中。

1.Monitor.Wait方法
当线程调用 Wait 时,它释放对象的锁并进入对象的等待队列,对象的就绪队列中的下一个线程(如果有)获取锁并拥有对对象的独占使用。Wait()就是交出锁的使用权,使线程处于阻塞状态,直到再次获得锁的使用权。
2.Monitor.Pulse方法
当前线程调用此方法以便向队列中的下一个线程发出锁的信号。接收到脉冲后,等待线程就被移动到就绪队列中。在调用 Pulse 的线程释放锁后,就绪队列中的下一个线程(不一定是接收到脉冲的线程)将获得该锁。pulse()并不会使当前线程释放锁。

当一个线程尝试着lock一个同步对象的时候,该线程就在就绪队列中排队。一旦没人拥有该同步对象,就绪队列中的线程就可以占有该同步对象。这也是我们平时最经常用的lock方法。为了其他的同步目的,占有同步对象的线程也可以暂时放弃同步对象,并把自己流放到等待队列中去,这就是Monitor.Wait;由于该线程放弃了同步对象,其他在就绪队列的排队者就可以进而拥有同步对象。比起就绪队列来说,在等待队列中排队的线程更像是二等公民:他们不能自动得到同步对象,甚至不能自动升舱到就绪队列。而Monitor.Pulse的作用就是开一次门,使得一个正在等待队列中的线程升舱到就绪队列;相应的Monitor.PulseAll则打开门放所有等待队列中的线程到就绪队列。

    class Program{static void Main(string[] args){new Thread(A).Start();new Thread(B).Start();new Thread(C).Start();Console.ReadLine();}static object lockObj = new object();static void A(){lock (lockObj) //进入就绪队列
            {Thread.Sleep(1000);Monitor.Pulse(lockObj);Monitor.Wait(lockObj); //自我流放到等待队列
            }Console.WriteLine("A exit...");}static void B(){Thread.Sleep(500);lock (lockObj) //进入就绪队列
            {Monitor.Pulse(lockObj);}Console.WriteLine("B exit...");}static void C(){Thread.Sleep(800);lock (lockObj) //进入就绪队列
            { }Console.WriteLine("C exit...");}}

假设线程A先得到了同步对象,它就登记到同步对象lockObj的“拥有者引用”中。线程B和C要求拥有同步对象,他们将在“就绪队列”排队,|--(拥有锁的线程) A | |--(就绪队列) B,C | |--(等待队列)。

线程A用Pulse发出信号,允许第一个正在"等待队列"中的线程进入到”就绪队列“。但由于等待列是空的,什么事也没有发生。线程A用Wait放弃同步对象,并把自己放入"等待队列"。B,C已经在就绪队列中,因此其中的一个得以获得同步对象(假定是B)。B成了同步对象的拥有者。C现在还是候补委员,可以自动获得空缺。而A则被关在门外,不能自动获得空缺。 |--(拥有锁的线程) B ||--(就绪队列) C | |--(等待队列) A

线程B用Pulse发出信号开门,第一个被关在门外的A被允许放入到就绪队列,现在C和A都成了候补委员,一旦同步对象空闲,都有机会得它。 |--(拥有锁的线程) B | |--(就绪队列) C,A | |--(等待队列)

class MyManualEvent{private object lockObj = new object();private bool hasSet = false;public void Set(){lock (lockObj){hasSet = true;Monitor.PulseAll(lockObj);}}public void WaitOne(){lock (lockObj){while (!hasSet){Monitor.Wait(lockObj);}}}}class Program2{static MyManualEvent myManualEvent = new MyManualEvent();static void Main(string[] args){ThreadPool.QueueUserWorkItem(WorkerThread, "A");ThreadPool.QueueUserWorkItem(WorkerThread, "B");Console.WriteLine("Press enter to signal the green light");Console.ReadLine();myManualEvent.Set();ThreadPool.QueueUserWorkItem(WorkerThread, "C");Console.ReadLine();}static void WorkerThread(object state){myManualEvent.WaitOne();Console.WriteLine("Thread {0} got the green light...", state);}}

我们看到了该玩具MyManualEvent实现了类库中的ManulaResetEvent的功能,但却更加的轻便,类库的ManulaResetEvent使用了操作系统内核事件机制,负担比较大(不算竞态时间,ManulaResetEvent是微秒级,而lock是几十纳秒级。例子的WaitOne中先在lock的保护下判断是否信号绿灯,如果不是则进入等待。因此可以有多个线程(比如例子中的AB)在等待队列中排队。当调用Set的时候,在lock的保护下信号转绿,并使用PulseAll开门放狗,将所有排在等待队列中的线程放入就绪队列,A或B(比如A)于是可以重新获得同步对象,从Monitor.Wait退出,并随即退出lock区块,WaitOne返回。随后B或A(比如B)重复相同故事,并从WaitOne返回。线程C在myManualEvent.Set()后才执行,它在WaitOne中确信信号灯早已转绿,于是可以立刻返回并得以执行随后的命令。该玩具MyManualEvent可以用在需要等待初始化的场合,比如多个工作线程都必须等到初始化完成后,接到OK信号后才能开工。该玩具MyManualEvent比起ManulaResetEvent有很多局限,比如不能跨进程使用,但它演示了通过基本的Monitor命令组合,达到事件机的作用。

C# Monitor实现相关推荐

  1. Monitor CodeForces - 846D ——二维前缀和

    Recently Luba bought a monitor. Mon itor is a rectangular matrix of size n × m. But then she started ...

  2. windows下 Source Monitor代码度量工具的使用

    windows下 Source Monitor代码度量工具的使用 引用链接: https://www.cnblogs.com/xuehanyu/p/4520965.html 1.总体介绍 Source ...

  3. C#中使用Monitor类、Lock和Mutex类来同步多线程的执行(转)

    C#中使用Monitor类.Lock和Mutex类来同步多线程的执行 在多线程中,为了使数据保持一致性必须要对数据或是访问数据的函数加锁,在数据库中这是很常见的,但是在程序中由于大部分都是单线程的程序 ...

  4. 转:C#中Monitor对象与Lock关键字的区别分析

    Monitor对象1.Monitor.Enter(object)方法是获取 锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取 ...

  5. 二维前缀和+差分 HDU6514 Monitor

    Monitor [ HDU - 6514 ] 题目大意:给你个n×m的区域,每个区域被选中标记为1,没被选中标记为0,然后给一些标记的区域,查询若干个区域问是否有0的 一道二维前缀和的题目,想通没什么 ...

  6. 设置树莓派的无线网卡为监听模式(monitor)

    先使用命令查看无线网卡的名字: ifconfig 结果如下: eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 1 ...

  7. Ambari安装之部署 (Metrics Collector和 Metrics Monitor) Install Pending ...问题

    问题详细描述如下:  Metrics Collector的解决办法 正在重新安装 Metrics  Monitor的解决办法 Metrics Collector和 Metrics Monitor 的I ...

  8. 集成服务监控器-green.monitor发布

    在大型企业应用开发中,一个项目经常需要依赖于多个项目集成,经常某个集成服务的升级或者不工作,会导致你所工作的服务也挂掉,甚至影响你的开发流程.你是否还在接到测试团队或者运维团队的某个Bug,而自己花费 ...

  9. Android官方命令深入分析之Device Monitor

    Android Device Monitor是一个提供了图形化界面的可以对Android应用进行调试和分析的独立的工具.Monitor工具不需要IDE环境,比如Android Studio.包括以下工 ...

  10. 文件翻译002片:Process Monitor帮助文档(Part 2)

    [筛选亮点] Process Monitor提供了一些方式来配置筛选器和高亮显示.         筛选器的包括与排除 您能够在筛选器中指定事件的属性,这样就能够令Process Monitor仅显示 ...

最新文章

  1. bgi::detail::path_intersection用法的测试程序
  2. MySQL备份与恢复——基于OUTFILE /LOAD DATA 逻辑备份恢复
  3. Android开发之刷新图片到相册 | 刷新视频到相册的方法区分发广播刷新方法
  4. Vue 教程第九篇—— 动画和过度效果
  5. SQL Server 2008 R2 安装
  6. 一个月薪两万的Web安全工程师要掌握哪些技能?
  7. python中文模糊关键词提取_用Python给你的文本提取关键词
  8. 浅论照明节能的系统设计
  9. 《天勤数据结构》笔记——假溢出和循环队列基本操作的实现(C/C++)
  10. HLW8032功率计+esp8266WiFi插座 mixly blynk
  11. R语言--Cox模型校准曲线原理(一)数据来源
  12. 关于idea debug step into不进入class文件或者自己想要略过的一些文件设置
  13. python画大对勾_多种方法告诉你!Word如何在方框中打对勾√和叉叉×
  14. Creator星球教程文章分类导航
  15. mysql有numeric类型吗_mysql数值类型 - numeric
  16. 新一代区块链手机“甲骨文”即将上市
  17. python实验结论_Python基础(上)实验报告
  18. Veritas针对通用数据保护再出手
  19. C语言:JSON格式详解
  20. android 9 所有应用的行为变更 和 以API28为目标的应用行为变更

热门文章

  1. oracle 删序列,oracle创建和删除序列
  2. 解决“chrome正受到自动测试软件的控制”信息栏显示问题(转)
  3. oracle x ksppi,求 x$ksppi 和x$ksppcv 得详细解释
  4. mysql 最近_关于mysql查询最近一条记录
  5. java会被rust替代吗_Rust 未来会成为主流的编程语言吗?
  6. 计算圆弧与矩形相交_【技术】新型七层矩形卷边工艺实践
  7. 国家计算机科学进展,主动网络安全的研究与进展
  8. 计算机英语翻译3000字,英语专四作文满分范文(二十八):计算机翻译
  9. scrapy 报错 no module named win32api 的解决方案
  10. VB案例:计算圆锥体积与面积