看看C#中用lock关键实现同步的原理 [转]

我们知道C#的关键字都对应着.net Framework的类型,对象,比如string 对应String。那么“Lock”是如何对应的呢,似乎不能像string一样那么明显地找一个对应的东西。但是通过使用“ildasm”工具,察看.net生成的中间代码,很容易能发现lock的工作机制:调用了System.Threading.Monitor.Enter/Exit来实现同步互斥。看下面一段代码:

 

我们知道C#的关键字都对应着.net Framework的类型,对象,比如string 对应String。那么“Lock”是如何对应的呢,似乎不能像string一样那么明显地找一个对应的东西。但是通过使用“ildasm”工具,察看.net生成的中间代码,很容易能发现lock的工作机制:调用了System.Threading.Monitor.Enter/Exit来实现同步互斥。看下面一段代码:

object someObject = new object();

. . .

private void button1_Click(object sender, EventArgs e)

{

    lock(someObject)

    {

        // Your code here

    }           }
利用ildasm打开编译过的代码,下面是生成的IL(中间语言)的代码:

.method private hidebysig instance void button1_Click(object sender, class [mscorlib]System.EventArgs e) cil managed

{

 // Code size       29 (0x1d)

 .maxstack 2

 .locals init ([0] object CS$2$0000)

 IL_0000: nop

 IL_0001: ldarg.0

 IL_0002: ldfld      object WindowsApplication1.Form1::someObject

 IL_0007: dup

 IL_0008: stloc.0

 IL_0009: call       void [mscorlib]System.Threading.Monitor::Enter(object)

 IL_000e: nop

 .try

 {

    IL_000f: nop

    IL_0010: nop

    IL_0011: leave.s    IL_001b

 } // end .try

 finally

 {

    IL_0013: ldloc.0

    IL_0014: call       void [mscorlib]System.Threading.Monitor::Exit(object)

    IL_0019: nop

    IL_001a: endfinally

 } // end handler

 IL_001b: nop

 IL_001c: ret

} // end of method Form1::button1_Click

 

 

 然后再看看显示调用System.Threading.Monitor.Enter/Exit进行同步的代码:(.NET4以前)

private void button1_Click(object sender, EventArgs e)

{

    System.Threading.Monitor.Enter(someObject);

 

    try

    {

        // Your code here

    }

    finally

    {

        System.Threading.Monitor.Exit(someObject);

    }        }

 

察看它的IL:

.method private hidebysig instance void button1_Click(object sender, class [mscorlib]System.EventArgs e) cil managed

{

 // Code size       34 (0x22)

 .maxstack 1

 IL_0000: nop

 IL_0001: ldarg.0

 IL_0002: ldfld      object WindowsApplication1.Form1::someObject

 IL_0007: call       void [mscorlib]System.Threading.Monitor::Enter(object)

 IL_000c: nop

 .try

 {

    IL_000d: nop

    IL_000e: nop

    IL_000f: leave.s    IL_0020

  } // end .try

 finally

 {

    IL_0011: nop

    IL_0012: ldarg.0

    IL_0013: ldfld      object WindowsApplication1.Form1::someObject

    IL_0018: call       void [mscorlib]System.Threading.Monitor::Exit(object)

    IL_001d: nop

    IL_001e: nop

    IL_001f: endfinally

 } // end handler

 IL_0020: nop

 IL_0021: ret     } // end of method Form1::button1_Click
通过比较很容易发现这两段代码生成的中间语言代码几乎是一样的,至此我们应该能够很明白lcok的工作原理了。

 

 

转自:http://space.itpub.net/22425738/viewspace-616320

 

但是,这种实现逻辑至少理论上有一个错误:当Monitor.Enter(lockObj);刚刚完成,还没有进入try区的时候,有可能从其他线程发出了Thread.Abort等命令,使得该线程没有机会进入try...finally。也就是说lockObj没有办法得到释放,有可能造成程序死锁。这也是Thread.Abort一般被认为是邪恶的原因之一。

DotNet4开始,增加了Monitor.Enter(object,ref bool)重载。而C#编译器会把lock展开为更安全的Monitor.Enter(object,ref bool)和Monitor.Exit:

C# code
lock(lockObj) { //...}
////相当于(DotNet 4):
bool lockTaken =false;
try
{
    Monitor.Enter(lockObj,ref lockTaken);
//
}
finally
 {
    if (lockTaken) Monitor.Exit(lockObj);
}


现在Monitor.TryEnter在try的保护下,“加锁”成功意味着“放锁”将得到finally的保护。


posted @ 2011-12-29 12:32  Silence_  阅读(1290)  评论(0)    收藏  举报