从IL认识关键字(四)

关键字

  上一篇研究了using关键字,在这篇我们研究一下lock关键字,在多线程,ASP.NET中涉及共享资源读写都会给线程代码加锁,保证资源正确读写。lock关键字结构也是try-finally结构。四篇随笔有3篇(foreach的集合遍历,using语句,lock语句)都是try-finally结构,在写着几篇文章时候,我发现以前处理finally考虑不全,所以还是熟悉最新的语法,使用它们不仅会使代码更加整洁,而且使代码更加健壮。

MSDN解释

 lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。

   一般线程加锁,我们有如下几个方法:

  1. lock关键字
  2. Monitor
  3. Mutex
  4. AutoResetEvent
  5. ManualResetEvent
  6. 其它  

  按照之前的经验,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性能测试工具得出调用方法

   从上图可见,依次调用

  1. new object()
  2. System.Threading.Monitor.Enter(object)
  3. System.Threading.Monitor.Exit(object)

  从另外一个方面证明了lock关键字内部机制。

下一篇关键字

   以上只是本人的理解与实践,如有错误不足之处希望理解包容,下一篇讨论linq关键字。如(var from where ...),若篇幅不够,开设多篇

原文地址:https://www.cnblogs.com/WilsonPan/p/2913847.html