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一个私有的静态引用成员变量来完成静态方法内的线程同步

  • 相关阅读:
    122. Best Time to Buy and Sell Stock II
    121. Best Time to Buy and Sell Stock
    72. Edit Distance
    583. Delete Operation for Two Strings
    582. Kill Process
    indexDB基本用法
    浏览器的渲染原理
    js实现txt/excel文件下载
    git 常用命令
    nginx进入 配置目录时
  • 原文地址:https://www.cnblogs.com/mingyongcheng/p/1996716.html
Copyright © 2011-2022 走看看