• 浅谈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 后,代码如下图所示:

    image

    从上图被标注的区域可以看到:一条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);
     }
    }

    结果如下:

    TryEnter1

    同步与互斥    示例二:

    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); 
     }
    }

    结果如下:

    TryEnter2

     

    参考文章:

    浅谈c#中使用lock的是与非      作者:Jeff Wong

    同步技术之Monitor                  作者:银河使者

     

    示例下载

    作者: XuGang   网名:钢钢
    出处: http://xugang.cnblogs.com
    声明: 本文版权归作者和博客园共有。转载时必须保留此段声明,且在文章页面明显位置给出原文连接地址!
  • 相关阅读:
    firstResponder
    形而上学
    du -h
    数据本地存贮与数据结构(对象)
    RFC
    oc语言源代码
    HTTP1.1协议中文版-RFC2616
    如何提高团队协作的效率
    iOS应用架构谈 开篇
    nginx版本如何选择?
  • 原文地址:https://www.cnblogs.com/xugang/p/1869818.html
Copyright © 2020-2023  润新知