c# lock 语句的编译后代码是什么样的

4次阅读

lock语句被编译为try-finally包裹的Monitor.Enter/Exit调用,确保异常时锁必释放;新版.NET默认用Monitor.Enter(obj, ref bool)保障原子性,旧版则依赖异常模拟。

c# lock 语句的编译后代码是什么样的

lock 语句会被编译成 try-finally + Monitor.Enter/Exit

你写的 lock(obj) { ... } 不会原样保留,C# 编译器(csc)会在 IL 层面把它重写为显式的 Monitor.EnterMonitor.Exit 调用,并包裹在 try...finally 块中。这是为了确保即使临界区抛出异常,锁也一定会被释放。

关键点:

  • Monitor.Enter 的返回值会被检查,如果为 false(表示未获取到锁),则不会进入 try 块,而是直接跳过整个临界区逻辑(但这种情况极少发生,通常只出现在带超时的重载里)
  • 标准 lock 对应的是无超时版本的 Monitor.Enter(Object),它不返回布尔值,所以编译器会用另一个重载:先调用 Monitor.Enter(object, ref bool)(.NET Core 2.0+ / .NET 5+ 默认行为),或在旧版中插入额外逻辑模拟原子性
  • 编译后的 finally 块里,Monitor.Exit 是无条件执行的,哪怕 Enter 失败也不会进 finally

看一个具体反编译例子

假设你有这段 C# 代码:

object _lock = new object(); lock (_lock) {     Console.WriteLine("in critical section"); }

ildasmdotnet ilc 查看其 Release 模式编译后的 IL(简化后)大致如下:

.try {     IL_0000: ldarg.0     IL_0001: ldfld object Test::<_lock>k__BackingField     IL_0006: stloc.0     IL_0007: ldloc.0     IL_0008: call void [System.Runtime]System.Threading.Monitor::Enter(object)     IL_000d: nop     IL_000e: ldstr "in critical section"     IL_0013: call void [System.Console]System.Console::WriteLine(string)     IL_0018: nop     IL_0019: leave.s IL_0025 } // end .try finally {     IL_001b: ldloc.0     IL_001c: call void [System.Runtime]System.Threading.Monitor::Exit(object)     IL_0021: nop     IL_0022: endfinally } // end handler

注意:leave.s 是跳转到 finally 之后的指令,不是跳过 finally —— 这正是保证 Exit 总被执行的关键。

为什么不能手动写 Monitor.Enter/Exit 替代 lock

看似等价,但手动写容易出错:

  • 漏掉 try-finally,导致异常时锁不释放 → 死锁风险
  • Enter 后、try 前就抛异常 → finally 还没建立,Exit 根本不会执行
  • 误用 Monitor.TryEnter 但忘记判断返回值,直接进临界区 → 逻辑错误
  • .NET 6+ 中,lock 还可能被 JIT 优化为轻量级锁(如偏向锁、自旋锁)路径,而手写调用绕过了这些优化层

lock 编译行为在不同 .NET 版本有差异

主要区别在锁获取的“原子性保障”实现方式:

  • .NET Framework 4.8 及更早:使用 Monitor.Enter(object) + 隐式异常处理模拟成功标志
  • .NET Core 2.0+ / .NET 5+:默认改用 Monitor.Enter(object, ref bool),由运行时保证调用本身是原子的,避免竞态条件
  • 所有版本都强制生成 try-finally 结构,这点没有例外

如果你在反编译时看到 ref bool 参数和两次 ldloca.s 指令,说明你正面对的是较新运行时的编译输出。这个细节常被忽略,但它直接影响锁失败时的行为可预测性。

text=ZqhQzanResources