分享

深入研究C#同步与互斥的lock方法与Monitor类的关系 - 学IT网 xueit.c...

 kittywei 2011-05-04

本文和大家分享一下浅谈C# 中的lock 方法与Monitor 类的关系_以及同步与互斥.
A  从单例模式说起
代码如下:
class Program{ static void Main(string[] args) { Singleton demo1 = Singleton.Init(); Singleton demo2 = Singleton.Init(); }} public class Singleton{ private Singleton() { }  private static Singleton instance = null; private static readonly object singleObj = new object();  public static Singleton Init() { if (null == instance) { lock (singleObj) //singleObj 不能使用instance 代替 { if (null == instance) { instance = new Singleton(); } } } return instance; }}关于单例模式,大家可以参考:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html
B  关于lock 方法
在以上单列模式的代码中:
如果将: private static readonly object singleObj = new object();修改为: private static readonly object singleObj = null;将在lock( ) 方法处,抛出未处理的异常:System.ArgumentNullException: 值不能为空!
●  lock 关键字将语句块标记为临界区;
●  lock( obj ) 方法中的obj 对象为:获取排他锁的指定对象。
                                     obj 对象为null 将导致 ArgumentNullException 异常。
 
●  lock 方法在MSIL 中会被编译成 Monitor.Enter( ) 和 Monitor.Exit( ) 。例如:
public static void MyLock(){ lock (typeof(Program)) { }}以上代码通过lock 语句使MyLock 同步,这个方法被编译成MSIL 后,代码如下图所示:

从上图被标注的区域可以看到:一条lock 语句被编译成了调用Monitor 的Enter 和Exit 的方法。
lock 的功能就相当于直接调用Monitor 的Entry 方法,并在结束后会自动调用Monitor 的Exit 方法解除锁定。
 

C  关于Monitor 类
●  Monitor 类属于System.Threading 命名空间;
●  Monitor 类提供同步对对象的访问的机制;
●  使用 Monitor 锁定对象(即引用类型)而不是值类型;
●  Monitor 类型对于多线程操作是安全的;
 
Monitor 类的示例一:
class Program2{ static void Main(string[] args) { MyMonitor1 mon_1 = new MyMonitor1(); mon_1.Test(); }} class MyMonitor1{ private object obj = new object();  public void Test()  { //开始锁定 System.Threading.Monitor.Enter(obj); try { //lock 的区域 } catch (Exception e) { // 异常处理代码 } finally { //解除锁定 System.Threading.Monitor.Exit(obj); } }}
Monitor 类的示例二:
class Program3{ static void Main(string[] args) { //多个线程调用Test方法 Thread t1 = new Thread(MyMonitor2.Test); Thread t2 = new Thread(MyMonitor2.Test); t1.Start(); t2.Start(); }}class MyMonitor2{ private static object obj = new object();  public static void Test() { //使用TryEntry方法设置一个锁定超时 if (Monitor.TryEnter(obj, 2000)) { try { Console.WriteLine("等待4秒开始"); Thread.Sleep(4000); Console.WriteLine("等待4秒结束"); } finally { //解除锁定 Monitor.Exit(obj); } } else  { Console.WriteLine("已超时2秒!"); }  }} 
D  关于同步与互斥
关于同步的问题,可以使用Monitor 类来解决。
在使用Monitor 类的时候,建议将Monitor.Enter( ) 方法替换成Monitor.TryEnter( ) 方法。
 
使用Monitor.Enter( ) 方法时,代码如下:
Monitor.Entry(lockObj);try{ // lockObj的同步区}catch(Exception e){ // 异常处理代码}finally{ Monitor.Exit(lockObj); // 解除锁定}注意:如果直接在C#源程序中使用Monitor类,就必须调用Exit方法来显式地解除锁定。
 
使用Monitor.TryEnter( ) 方法时,代码如下:
if(Monitor.TryEntry(lockObj, 1000)){ try { } finally { Monitor.Exit(lockObj); }}else{ // 超时后的处理代码}注意:使用TryEntry方法设置一个锁定超时,单位是毫秒。
上面的代码设置了锁定超时时间为1秒。
如果在1秒钟之内,lockObj 还未被解锁,TryEntry 方法就会返回 false;
如果在1秒钟之内,lockObj 被解锁,TryEntry 方法就会返回 true。
这样,可以使用TryEntry 方法来避免死锁。
同步与互斥    示例一:
class Program4{ static void Main(string[] args) { Thread A = new Thread(TestClass1.GetA); A.Name = "Thread_A "; Thread B = new Thread(TestClass1.GetB); B.Name = "Thread_B "; A.Start(); B.Start(); }} class TestClass1{ private static object resource_A = new object(); private static object resource_B = new object();  public static void GetA() { MyWrite("in GetA()"); if (Monitor.TryEnter(resource_A, 2000)) { MyWrite("get resource_A"); GetB(); Thread.Sleep(2000); Monitor.Exit(resource_A); MyWrite("exit resource_A"); } else { MyWrite("no has resource_A"); } }  public static void GetB() { MyWrite("in GetB()"); if (Monitor.TryEnter(resource_B, 1000)) { MyWrite("get resource_B"); GetA(); Thread.Sleep(1000); Monitor.Exit(resource_B); MyWrite("exit resource_B"); } else { MyWrite("no has resource_B"); } }  //自定义打印方法 private static void MyWrite(string str) { Console.WriteLine(Thread.CurrentThread.Name + str); }}结果如下:
 
 
同步与互斥    示例二:
class Program5{ static void Main(string[] args) { Thread A = new Thread(TestClass2.GetA); A.Name = "Thread_A "; Thread B = new Thread(TestClass2.GetB); B.Name = "Thread_B "; A.Start(); B.Start(); }} class TestClass2{ //排他锁的对象 A private static object resource_A = new object(); //排他锁的对象 B private static object resource_B = new object();  public static void GetA() { MyWrite("in GetA()"); if (Monitor.TryEnter(resource_A, 1000)) { MyWrite("get resource_A"); Thread.Sleep(1000); //GetB(); if (Monitor.TryEnter(resource_B, 2000)) { Monitor.Exit(resource_B); } Monitor.Exit(resource_A); MyWrite("exit resource_A"); } else { MyWrite("no has resource_A"); } }  public static void GetB() { MyWrite("in GetB()"); if (Monitor.TryEnter(resource_B, 1000)) { MyWrite("get resource_B"); Thread.Sleep(1000); //GetA(); if (Monitor.TryEnter(resource_A, 2000)) { Monitor.Exit(resource_A); } Monitor.Exit(resource_B); MyWrite("exit resource_B"); } else { MyWrite("no has resource_B"); } }  //自定义打印方法 private static void MyWrite(string str) { Console.WriteLine(Thread.CurrentThread.Name + str);  }}结果如下:
 
 
参考文章:
浅谈c#中使用lock的是与非      作者:Jeff Wong
同步技术之Monitor                  作者:银河使者
 
示例下载

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多