NET面试题:C#中的lock关键字有何作用
标签:
更多.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的值为:0i1自增后为:1i1的值为:1i1自增后为:2i1的值为:2i1自增后为:3i1的值为:3i1自增后为:4i1的值为:4i1自增后为:5开始测试成员方法的同步i2的值为:0i2自增后为:1i2的值为:1i2自增后为:2i2的值为:2i2自增后为:3i2的值为:3i2自增后为:4i2的值为:4i2自增后为:5可以看到,线程同步被很好地保证了。这里需要强调的是,线程同步本身违反了多线程并行运行的原则,所以读者在使用线程同步时应该尽量做到把lock加在最小的程序块上。如果一个方法有大量的代码需要线程同步,那就需要重新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程本身的开销也是相当大的。对静态方法的同步,一般采用静态私有的引用成员,而对成员方法的同步,一般采用私有的引用成员。读者需要注意静态和非静态成员的使用和把同步对象申明为私有,这都是保证线程同步高效并且正确的关键点。 答案C#中的lock关键字实质是调用Monitor.Enter和Monitor.Exit两个方法的简化语法,功能上其实现了进入和退出某个对象的同步。在通常情况下,可以通过lock一个私有的引用成员变量来完成成员方法内的线程同步,而通过lock一个私有的静态引用成员变量来完成静态方法内的线程同步