C# Monitor:锁定资源

Posted CSharp编程大全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# Monitor:锁定资源相关的知识,希望对你有一定的参考价值。

C#中, 通过System.Threading.Monitor类可以实现多线程中对某些代码块的同步访问,以确保数据的安全性。


object obj=new object();

Monitor在锁对象obj上会维持两个线程队列R和W以及一个引用T :

(1) T是对当前获得了obj锁的线程的引用(设此线程为CurrThread); 

(2) R为就绪队列, 其上的线程已经准备好获取obj锁。当obj锁被CurrThread释放后(CurrThread可通过Monitor.Exit(obj)或 Monitor.Wait(obj)来释放其所获的obj锁)这些线程就会去竞争obj锁,获得obj锁的线程将被T引用; 线程调用Monitor.Enter(obj)或Monitor.TryEnter(obj)将会使该线程直接进入R队列。

(3) W为等待队列,其上的线程是因为调用了Monitor.Wait(obj)而进入W队列的;W上的线程不会被OS直接调度执行,也就是说它们没有准备好获取obj锁,就是说在等待队列上的线程不能去获得obj锁。当前获得obj锁的线程CurrThread调用Monitor.Pulse(obj)或Monitor.PulseAll(obj)后会使W队列中的第一个等待线程或所有等待线程被移至R队列,这时被移至R队列的这些线程就有机会被OS直接调度执行,也就是有可以去竞争obj锁。

lock 关键字

lock 关键字可以作为Monitor类的一个替代。下面两个代码块是等效的:

Monitor.Enter(this);//...Monitor.Exit(this);
lock (this){ //...}

在这里,object 值与 lock 中的 object 值是一样的。

简而言之,lock 的写法是 Monitor 类的一种简写。

【实例】将上一节《C# lock》实例中的 lock 关键字替换成 Monitor 类。

根据题目要求,代码如下。

class Program{ public void PrintEven() { Monitor.Enter(this); try { for(int i = 0; i <= 10; i = i + 2) { Console.WriteLine(Thread.CurrentThread.Name + "--" + i); } } finally { Monitor.Exit(this); } } public void PrintOdd() { Monitor.Enter(this); try { for(int i = 1; i <= 10; i = i + 2) { Console.WriteLine(Thread.CurrentThread.Name + "--" + i); } } finally { Monitor.Exit(this); } } static void Main(string[] args) { Program program = new Program(); ThreadStart ts1 = new ThreadStart(program.PrintOdd); Thread t1 = new Thread(ts1); t1.Name = "打印奇数的线程"; t1.Start(); ThreadStart ts2 = new ThreadStart(program.PrintEven); Thread t2 = new Thread(ts2); t2.Name = "打印偶数的线程"; t2.Start(); }}

运行该程序,效果如下图所示。

Monitor 类的TryEnter() 方法在尝试获取一个对象上的显式锁方面和 Enter() 方法类似。然而,它不像Enter()方法那样会阻塞执行。如果线程成功进入关键区域那么TryEnter()方法会返回true.


Monitor 类的用法虽然比 lock 关键字复杂,但其能添加等待获得锁定的超时值,这样就不会无限期等待获得对象锁。

使用 TryEnter() 方法可以给它传送一个超时值,决定等待获得对象锁的最长时间。

使用 TryEnter() 方法设置获得对象锁的时间的代码如下。

Monitor.TryEnter(object, 毫秒数 );

该方法能在指定的毫秒数内结束线程,这样能避免线程之间的死锁现象。

此外,还能使用 Monitor 类中的 Wait() 方法让线程等待一定的时间,使用 Pulse() 方法通知处于等待状态的线程。


 C#中Monitor和Lock简介及区别

1.Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit())。

  2.Monitor的常用属性和方法:

    Enter(Object) 在指定对象上获取排他锁。

    Exit(Object) 释放指定对象上的排他锁。

    IsEntered 确定当前线程是否保留指定对象锁。

    Pulse 通知等待队列中的线程锁定对象状态的更改。

    PulseAll 通知所有的等待线程对象状态的更改。

    TryEnter(Object) 试图获取指定对象的排他锁。

    TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否得到了该锁。

    Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。

                                       Lock关键字

  1.Lock关键字实际上是一个语法糖,它将Monitor对象进行封装,给object加上一个互斥锁,A进程进入此代码段时,会给object对象加上互斥锁,此时其他B进程进入此代码段时检查object对象是否有锁?如果有锁则继续等待A进程运行完该代码段并且解锁object对象之后,B进程才能够获取object对象为其加上锁,访问代码段。

  2.Lock关键字封装的Monitor对象结构如下:

 try { Monitor.Enter(obj); dosomething(); } catch(Exception ex) {  } finally { Monitor.Exit(obj); }

 3.锁定的对象应该声明为private static object obj = new object();尽量别用公共变量和字符串、this、值类型。

Monitor和Lock的区别

  1.Lock是Monitor的语法糖。

  2.Lock只能针对引用类型加锁。

  3.Monitor能够对值类型进行加锁,实质上是Monitor.Enter(object)时对值类型装箱。

  4.Monitor还有其他的一些功能。

本文代码示例:

 class Program { private static object obj = new object(); public void LockSomething() { lock (obj) { dosomething(); } } public void MonitorSomeThing() { try { Monitor.Enter(obj); dosomething(); } catch(Exception ex) {  } finally { Monitor.Exit(obj); } }
public void dosomething() { //做具体的事情 } }


以上是关于C# Monitor:锁定资源的主要内容,如果未能解决你的问题,请参考以下文章

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

lock与monitor的区别

线程同步基础知识点

编写高质量代码改善C#程序的157个建议——建议72:在线程同步中使用信号量

多线程(基础篇3)

C#互斥访问临界资源.lock 怎么超时