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, , 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();
Monitor.Pulse(lockObj);
Monitor.Wait(lockObj); //自我流放到等待队列
}
Console.WriteLine("A exit...");
}
static void B()
{
Thread.Sleep();
lock (lockObj) //进入就绪队列
{
Monitor.Pulse(lockObj);
}
Console.WriteLine("B exit...");
}
static void C()
{
Thread.Sleep();
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. C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

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

  2. API Monitor简介(API监控工具)

    API Monitor是一个免费软件,可以让你监视和控制应用程序和服务,取得了API调用. 它是一个强大的工具,看到的应用程序和服务是如何工作的,或跟踪,你在自己的应用程序的问题. 64位支持 API ...

  3. 创建 Monitor 并测试 - 每天5分钟玩转 OpenStack(124)

    前面我们创建了 Pool,VIP 并添加了 Member.今天将创建 Monitor,然后测试 LBaaS 是否能够正常工作. 创建 Monitor LBaaS 可以创建 monitor,用于监控 P ...

  4. 11g新特性:Health Monitor Checks

    一.什么是Health Monitor ChecksHealth Monitor Checks能够发现文件损坏,物理.逻辑块损坏,undo.redo损坏,数据字典损坏等等.Health Monitor ...

  5. Guava monitor

    Guava的com.google.util.concurrent类库提供了相对于jdk java.util.concurrent包更加方便实用的并发类,Monitor类就是其中一个.Monitor类在 ...

  6. DAC Usage3:Monitor Data-tier Applications

    If you deploy a DAC to a managed instance of the Database Engine, information about the deployed DAC ...

  7. C# 对象锁——Monitor

    Monitor里边有一些static方法,可以用于在对象上获取同步锁,来进行一些进程同步控制操作 用法及注意点如下: using System; using System.Collections.Ge ...

  8. Microsoft Message Analyzer (微软消息分析器,“网络抓包工具 - Network Monitor”的替代品)官方正式版现已发布

    来自官方日志的喜悦 被誉为全新开始的消息分析器时代,由MMA为您开启,博客原文写的很激动,大家可以点击这里浏览:http://blogs.technet.com/b/messageanalyzer/a ...

  9. sql monitor生成不了报告& FFS hint不生效两个问题思考

    事情的发生就是这么偶然,一步步的深入才能汲取到更深入的知识~~ -------------------START------------------------------------------- ...

  10. [转载]抓包,端口镜像,monitor session命令(转)

    原文地址:抓包,端口镜像,monitor session命令(转)作者:浮云皓月 一.SPAN简介 SPAN技术主要是用来监控交换机上的数据流,大体分为两种类型,本地SPAN和远程SPAN. --Lo ...

随机推荐

  1. js将时间戳转为时间格式

    时间戳转时间格式 //分钟 let timeM= parseInt(msg/1000/60%60); if(timeM<10){ timeM="0"+timeM; } //秒 ...

  2. Ubuntu18.04上安装Docker-Compose

    1.进入https://github.com/docker/compose/releases 查看最新版本,当前版本为1.23.1 sudo curl -L https://github.com/do ...

  3. sql 的一些总结

    如果用到“每”  就要用到group  by   例:每个部门有多少人,就要用到分组技术 聚合函数一般作用在多条记录上 having 是分组厚的筛选条件,分组厚的数据组内再筛选,where 则是在分组 ...

  4. js读取cookie,并利用encrypt和decrypt加密和解密方法

    以下为支持encrypt和decrypt加密和解密方法 eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a ...

  5. POJ 1056 IMMEDIATE DECODABILITY 【Trie树】

    <题目链接> 题目大意:给你几段只包含0,1的序列,判断这几段序列中,是否存在至少一段序列是另一段序列的前缀. 解题分析: Trie树水题,只需要在每次插入字符串,并且在Trie树上创建节 ...

  6. poj 3368 Frequent values(经典)【RMQ】

    <题目链接> 题目大意: 给你一个长度为n的序列,这个序列每个数都有一个值,接下来进行q次询问,问在指定区间内出现次数最多的数出现了几次. 解题分析: 因为该序列是非降序的,所以该序列中的 ...

  7. Linux学习之日志管理(二十一)

    Linux学习之日志管理 目录 日志管理 日志服务 rsyslogd的新特点 启动日志服务 常见日志的作用 日志文件的一般格式 rsyslogd日志服务 /etc/rsyslog.conf配置文件 服 ...

  8. JDBC概述

    什么是持久化(persistence):持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固 ...

  9. 从函数式编程到Ramda函数库(一)

    函数式编程是种编程方式,它将电脑运算视为函数的计算.函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值).和指令式编程相比, ...

  10. 解决UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range

    字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(en ...