關鍵字
上一篇研究了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 ...),若篇幅不夠,開設多篇