lock是Monitor的语法糖,编译后转为Monitor.Enter和Monitor.Exit的try-finally结构,确保异常时锁能释放;Monitor提供超时、Wait/Pulse等更细粒度控制;建议一般用lock,复杂场景选Monitor,并注意锁对象安全与配对调用。

在C#中,lock 和 Monitor 都用于实现线程同步,防止多个线程同时访问共享资源。它们本质上是紧密相关的,但使用方式和灵活性有所不同。理解它们的联系与区别,有助于写出更安全、高效的多线程代码。
lock 是 Monitor 的语法糖
lock 关键字实际上是 Monitor.Enter 和 Monitor.Exit 的简写形式。当你写一段 lock 代码时,编译器会自动将其转换为使用 Monitor 的 try-finally 结构,确保即使发生异常也能正确释放锁。
例如,下面这段代码:
lock (obj){
// 临界区
}
会被编译器翻译成:
bool lockTaken = false;try
{
Monitor.Enter(obj, ref lockTaken);
// 临界区
}
finally
{
if (lockTaken) Monitor.Exit(obj);
}
这种转换保证了线程安全和锁的正确释放,避免死锁或资源泄漏。
Monitor 提供更精细的控制
虽然 lock 使用简单,但 Monitor 类提供了更多高级功能,适合复杂场景:
-
超时机制:使用
Monitor.TryEnter(obj, timeout)可以尝试获取锁并在指定时间内失败,避免无限等待。 -
条件等待:通过
Monitor.Wait()让线程暂时释放锁并等待通知,配合Monitor.Pulse()或Monitor.PulseAll()唤醒其他线程,实现生产者-消费者模式。 - 可重入性支持:同一个线程可以多次进入同一锁,Monitor 能正确计数并要求对应次数的 Exit。
这些特性在 lock 中无法直接使用,因为 lock 不暴露 Wait/Pulse 或超时控制。
使用建议与注意事项
大多数情况下推荐使用 lock,因为它简洁、安全,不易出错。但在需要等待/通知机制或超时处理时,应选择 Monitor。
- 锁对象应为私有、引用类型,且不为 null。常见做法是声明一个 private readonly object 作为锁目标。
- 避免锁定 this、typeof(MyClass) 或字符串常量,可能引发意外的锁竞争或死锁。
- 不要在 lock 外调用 Monitor.Exit,否则可能抛出 SynchronizationLockException。
- 使用 Monitor 时务必配对 Enter/Exit 或使用 TryEnter/Exit,并放在 finally 块中。
性能与替代方案
lock 和 Monitor 基于操作系统互斥机制,属于重量级同步原语。在高并发场景下,可考虑更轻量的替代方案:
- Interlocked:适用于简单的原子操作(如自增、交换)。
- ReaderWriterLockSlim:读多写少场景下提升并发性能。
- async/await + SemaphoreSlim:异步编程中控制并发数。
但 lock 和 Monitor 仍是通用线程同步的基础工具。
基本上就这些。lock 简单安全,Monitor 强大灵活,根据需求选择即可。理解它们的底层机制,才能更好应对复杂的并发问题。









