zoukankan      html  css  js  c++  java
  • NET面试题:C#中的lock关键字有何作用

    NET面试题:C#中的lock关键字有何作用
    2011-03-01 08:42:04

    更多.net面试题,.net电子书,.net教学视频请参考"51CTO提醒您,请勿滥发广告!"

    NET面试题:C#中的lock关键字有何作用
    作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字。但如何正确并有效地使用lock却是能否高效地达到同步要求的关键。正因为如此,程序员需要完全理解lock究竟为程序做了什么。
      所涉及的知识点
    · lock的等效代码
    · System.Threading.Monitor类型的作用和使用方法
      分析问题
    1.lock的等效代码
    在.NET的多线程程序中,经常会遇到lock关键字来控制同步,比如下列代码:
    private object o = new object();
    public void Work()
    {
    lock(o)
    {
      //做一些需要线程同步的工作
    }
    }
    事实上,lock这个关键字是C#为方便程序员而定义的语法,它等效于安全地使用System.Threading.Monitor类型。上面的代码就直接等效于下面的代码:
    private object o = new object();
    public void Work()
    {
    //这里很重要,是为了避免直接使用私有成员o,而导致线程不安全
    Object temp = o;
    System.Threading.Monitor.Enter(temp);
    try
    {
      //做一些需要线程同步的工作
    }
    finally
    {
      System.Threading.Monitor.Exit(temp);
    }
    }
    正如读者所看到的,真正实现了线程同步功能的,就是System.Threading.Monitor类型,lock关键字只是用来代替调用Enter、Exit方法,并且将所有的工作包含在try块内以保证其最终退出同步。
    2.System.Threading.Monitor类型的作用和使用
    在前文中笔者已经提到了,Monitor类型的Enter和Exit方法用来实现进入和退出对象的同步。具体来说,当Enter方法被调用时,对象的同步索引将被检查,并且.NET将负责一系列的后续工作来保证对象访问时线程的同步,而Exit方法的调用,则保证了当前线程释放该对象的同步块。
    代码7-13演示了如何利用lock关键字(也就是Monitor类型)来实现线程同步,具体定义了一个包含需要同步执行方法的类型。
    代码7-13  线程同步:UseLock.cs
        /// <summary>
        /// 演示同步锁
        /// </summary>
        public class Lock
        {
            //用来在静态方法中同步
            private static Object o1 = new object();
            //用来在成员方法中不同
            private Object o2 = new object();
            //成员变量
            private static int i1 = 0;
            private int i2 = 0;
            /// <summary>
            /// 测试静态方法的同步
            /// </summary>
            /// <param name="state">状态对象</param>
            public static void Increment1(Object state)
            {
                lock (o1)
                {
                    Console.WriteLine("i1的值为:{0}", i1.ToString());
                    //这里刻意制造线程并行机会
                    //来检查同步的功能
                    Thread.Sleep(200);
                    i1++;
                    Console.WriteLine("i1自增后为:{0}", i1.ToString());
                }
            }
            /// <summary>
            /// 测试成员方法的同步
            /// </summary>
            /// <param name="state">状态对象</param>
            public void Increment2(Object state)
            {
                lock (o2)
                {
                    Console.WriteLine("i2的值为:{0}", i2.ToString());
                    //这里刻意制造线程并行机会
                    //来检查同步的功能
                    Thread.Sleep(200);
                    i2++;
                    Console.WriteLine("i2自增后为:{0}", i2.ToString());
                }
            }      
        }
    这样,在main方法中调用该类型对象的方法和其静态方法,测试其同步的效果,如代码7-14所示。
    代码7-14  线程同步:UseLock.cs
    /// <summary>
    /// 程序入口
    /// </summary>
    class MainClass
    {
        /// <summary>
        /// 测试同步效果
        /// </summary>
        static void Main(string[] args)
        {
            //开始多线程
            Console.WriteLine("开始测试静态方法的同步");
            for (int i = 0; i < 5; i++)
            {
                Thread t = new Thread(Lock.Increment1);
                t.Start();
            }
            //这里等待线程执行结束
            Thread.Sleep(5*1000);
            Console.WriteLine("开始测试成员方法的同步");
            Lock l = new Lock();
            //开始多线程
            for (int i = 0; i < 5; i++)
            {
                Thread t = new Thread(l.Increment2);
                t.Start();
            }
            Console.Read();
        }
    }
    下面是程序的执行结果:
    开始测试静态方法的同步
    i1的值为:0
    i1自增后为:1
    i1的值为:1
    i1自增后为:2
    i1的值为:2
    i1自增后为:3
    i1的值为:3
    i1自增后为:4
    i1的值为:4
    i1自增后为:5
    开始测试成员方法的同步
    i2的值为:0
    i2自增后为:1
    i2的值为:1
    i2自增后为:2
    i2的值为:2
    i2自增后为:3
    i2的值为:3
    i2自增后为:4
    i2的值为:4
    i2自增后为:5
    可以看到,线程同步被很好地保证了。这里需要强调的是,线程同步本身违反了多线程并行运行的原则,所以读者在使用线程同步时应该尽量做到把lock加在最小的程序块上。如果一个方法有大量的代码需要线程同步,那就需要重新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程本身的开销也是相当大的。
    对静态方法的同步,一般采用静态私有的引用成员,而对成员方法的同步,一般采用私有的引用成员。读者需要注意静态和非静态成员的使用和把同步对象申明为私有,这都是保证线程同步高效并且正确的关键点。
      答案
    C#中的lock关键字实质是调用Monitor.Enter和Monitor.Exit两个方法的简化语法,功能上其实现了进入和退出某个对象的同步。在通常情况下,可以通过lock一个私有的引用成员变量来完成成员方法内的线程同步,而通过lock一个私有的静态引用成员变量来完成静态方法内的线程同步

  • 相关阅读:
    AE的空间分析(转载)
    arcengine之版本管理
    执行 bower -v 时出现内部错误
    layui中获取全部提交的数据
    个推 简单的应用(安卓)
    在layui中,新的页面怎么获取另一个页面传过来的数据,并可以对数据进行判断,layui中的后台分页(table)。
    layui基本使用(动态获取数据,并把需要的数据传到新打开的窗口)
    layui的分页使用(前端分页)
    idea的热部署
    Lucene的步骤
  • 原文地址:https://www.cnblogs.com/mingyongcheng/p/1996716.html
Copyright © 2011-2022 走看看