探究c# lock
今天早上阅读前辈的代码,看到了这么一段代码,如下所示:
1 lock("Execute") 2 { 3 string sqlStr = sbSQLScript.ToString(); 4 }
看到第一句,我就怀疑了,c#当中的lock可以这么用吗?这是个什么用法,我第一次看到。我百度了下相关的技术资料。lock 一个引用类型,这没啥问题。问题是这个对象是个字符串。字符串在公共运行时clr中暂留,这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。
这一句话,来源于网络上别人的文章,需要验证,看是不是相同的字符串表示同一个对象?
static void Main(string[] args) { string a = "wbq"; string b = "wbq"; if (a == b && a.Equals(b) && object.ReferenceEquals(a,b)) { Console.WriteLine("i am wbq,i am testing lock"); } }
运行结果:
那是不是字符串内容相同了,就一定是同一个对象呢?
1 static void Main(string[] args) 2 { 3 string a = "wbq"; 4 string b = "w"; 5 b += "bq"; 6 7 if (a == b) 8 { 9 Console.WriteLine("我们的值相同"); 10 11 if (object.ReferenceEquals(a, b)) 12 { 13 14 Console.WriteLine("i am wbq,i am testing lock"); 15 } 16 else 17 { 18 Console.WriteLine("但是我们是两个不同的对象"); 19 } 20 } 21 }
运行结果:
看到这个结果,是不是一开始很吃惊呢?是的,刚开始的时候,是很吃惊。后来想到,不是前辈教导我们:如果拼接大量的字符串会影响性能,改用StringBuilder吗?刚才的程序演示,果然揭示了这一真理,拼接字符串会产生新的对象,所以如果大量拼接,则会产生大量对象,对象多了,事情就多了。垃圾回收器GC可就忙了,它太忙,势必影响我们程序的性能。另外一方面,内存资源消耗的多了。有人说了,现在的电脑硬件很好什么的,这个不用操心,我就想说,内存还是比较稀缺的资源。水涨船高嘛,内存大了,如今的程序胃口也大开,占用的内存也多了。
好了,言归正传,我们今天研究的主题是lock,不是string。
据说lock是个语法糖,它的真面目是 monitor.enter 结构,这个要怎么看才能知道呢?下面是代码:
1 class MainApp 2 { 3 class Program 4 { 5 private static readonly object lock4 = new object(); 6 static void Main(string[] args) 7 { 8 9 lock (lock4) 10 { 11 Console.WriteLine("i am wbq,i am testing lock"); 12 } 13 14 Console.Read(); 15 } 16 }
通过查看IL代码,可以看到:
1 .method private hidebysig static void Main(string[] args) cil managed 2 { 3 .entrypoint 4 // 代码大小 57 (0x39) 5 .maxstack 2 6 .locals init (bool V_0, 7 object V_1, 8 bool V_2) 9 IL_0000: nop 10 IL_0001: ldc.i4.0 11 IL_0002: stloc.0 12 .try 13 { 14 IL_0003: ldsfld object ConsoleApplication1.MainApp/Program::lock4 15 IL_0008: dup 16 IL_0009: stloc.1 17 IL_000a: ldloca.s V_0 18 IL_000c: call void [mscorlib]System.Threading.Monitor::Enter(object, 19 bool&) 20 IL_0011: nop 21 IL_0012: nop 22 IL_0013: ldstr "i am wbq,i am testing lock" 23 IL_0018: call void [mscorlib]System.Console::WriteLine(string) 24 IL_001d: nop 25 IL_001e: nop 26 IL_001f: leave.s IL_0031 27 } // end .try 28 finally 29 { 30 IL_0021: ldloc.0 31 IL_0022: ldc.i4.0 32 IL_0023: ceq 33 IL_0025: stloc.2 34 IL_0026: ldloc.2 35 IL_0027: brtrue.s IL_0030 36 IL_0029: ldloc.1 37 IL_002a: call void [mscorlib]System.Threading.Monitor::Exit(object) 38 IL_002f: nop 39 IL_0030: endfinally 40 } // end handler 41 IL_0031: nop 42 IL_0032: call int32 [mscorlib]System.Console::Read() 43 IL_0037: pop 44 IL_0038: ret 45 } // end of method Program::Main
第18行 Threading.Monitor::Enter,第37行 System.Threading.Monitor::Exit,关于IL代码的一些语法规则,我打算另外阐述。