从IL认识关键字(四)
关键字
上一篇研究了using关键字,在这篇我们研究一下lock关键字,在多线程,ASP.NET中涉及共享资源读写都会给线程代码加锁,保证资源正确读写。lock关键字结构也是try-finally结构。四篇随笔有3篇(foreach的集合遍历,using语句,lock语句)都是try-finally结构,在写着几篇文章时候,我发现以前处理finally考虑不全,所以还是熟悉最新的语法,使用它们不仅会使代码更加整洁,而且使代码更加健壮。
MSDN解释
lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。
一般线程加锁,我们有如下几个方法:
- lock关键字
- Monitor
- Mutex
- AutoResetEvent
- ManualResetEvent
- 其它
按照之前的经验,C#的语法糖都不是新的东西,都是把现有的东西包装一下,这里lock关键字究竟用哪个对象做线程同步,这里我们需要IL。
C# IL Code
准备一个简单的例子
void LockIL() { lock(this) { Console.WriteLine("Hello World"); } }
利用Reflector反编译得出下面IL
.method private hidebysig instance void LockIL() cil managed { .maxstack 2 .locals init ( [0] class Test.Program program) L_0000: nop L_0001: ldarg.0 L_0002: dup L_0003: stloc.0 L_0004: call void [mscorlib]System.Threading.Monitor::Enter(object) L_0009: nop L_000a: nop L_000b: ldstr "Hello World" L_0010: call void [mscorlib]System.Console::WriteLine(string) L_0015: nop L_0016: nop L_0017: leave.s L_0021 L_0019: ldloc.0 L_001a: call void [mscorlib]System.Threading.Monitor::Exit(object) L_001f: nop L_0020: endfinally L_0021: nop L_0022: ret .try L_000a to L_0019 finally handler L_0019 to L_0021 }
这段IL比较简单,一个try-finally结构,在try之前Enter,try里面输出(lock括号),finally里Exit。翻译如下面代码
void LockCode() { Program program; System.Threading.Monitor.Enter(program = this); try { Console.WriteLine("Hello World"); } finally { System.Threading.Monitor.Exit(program); } }
V3.5
这个是.Net3.5版本编译下代码,.Net4.0版本编译代码如下,IL代码就不在这里贴出来,有兴趣的园友可以自己反编译看看。相对于3.5代码,4.0的代码更加合理,只有Enter调用成功才调用Exit。
void LockCode() { bool flag = false; Program program = this; try { System.Threading.Monitor.Enter(program ,ref flag); Console.WriteLine("Hello World"); } finally { if(flag == true) { System.Threading.Monitor.Exit(program); } } }
V4.0
验证
下面简单例子
static void Main(string[] args) { lock (new object()) { } }
利用VS性能测试工具得出调用方法
从上图可见,依次调用
- new object()
- System.Threading.Monitor.Enter(object)
- System.Threading.Monitor.Exit(object)
从另外一个方面证明了lock关键字内部机制。
下一篇关键字
以上只是本人的理解与实践,如有错误不足之处希望理解包容,下一篇讨论linq的关键字。如(var from where ...),若篇幅不够,开设多篇