c# monitor 和 lock 有什么区别

来源:这里教程网 时间:2026-02-21 17:37:16 作者:

lock 就是 Monitor 的语法糖,别把它当两个东西学

你写

lock(obj) { ... }
,编译器立刻把它翻译成
Monitor.Enter
+
try-finally
+
Monitor.Exit
。不是“类似”,是**完全等价**——IL 层面一模一样。所以别纠结“哪个更底层”,
lock
不是替代品,它是
Monitor
的安全封装。

编译后自动加
try-finally
,哪怕临界区抛异常,锁也必释放(不用你操心)
不支持超时、等待、唤醒——这些功能根本没暴露出来 只接受引用类型作为锁对象;传值类型会隐式装箱,每次装箱生成新对象,导致锁失效甚至死锁

Monitor.TryEnter 能帮你躲开死锁,lock 做不到

当你不确定锁会不会被长时间占用(比如依赖外部服务、数据库慢查询、或另一个可能卡住的线程),用

Monitor.TryEnter(obj, timeout)
是唯一靠谱的选择。它返回
bool
:成功拿到锁就干正事,失败就走降级逻辑,而不是傻等。

timeout
单位是毫秒,传
0
表示“试试看,不等”;传
-1
等价于无限等待(和
Monitor.Enter
一样)
必须配对调用
Monitor.Exit
,且只能在
lockTaken == true
时调用,否则抛
SynchronizationLockException
常见错误:忘了在
finally
里检查
lockTaken
,直接
Monitor.Exit
→ 运行时报错
bool lockTaken = false;
try
{
    if (Monitor.TryEnter(_syncObj, 500)) // 等最多 500ms
    {
        lockTaken = true;
        // 执行临界区
    }
    else
    {
        Log.Warn("获取锁超时,跳过处理");
        return;
    }
}
finally
{
    if (lockTaken) Monitor.Exit(_syncObj);
}

Wait/Pulse 只能在 Monitor 里用,lock 完全没这能力

如果你在写生产者-消费者、信号量控制、状态驱动的协同逻辑(比如“等数据来了再处理”),

lock
直接出局。
Monitor.Wait
会**主动释放锁并挂起当前线程**,直到另一个线程调用
Monitor.Pulse
Monitor.PulseAll
唤醒它——这是协作式同步的核心机制。

Wait
必须在已持有锁的前提下调用,否则抛
SynchronizationLockException
Pulse
只唤醒一个等待线程;
PulseAll
唤醒全部——但唤醒不等于立即执行,它们还得重新竞争锁
典型陷阱:先
Pulse
Wait
(即“信号丢失”),必须用循环条件检查 +
while
包裹
Wait

锁对象选错,lock 和 Monitor 都会翻车

无论用哪个,锁对象本身出问题,同步就形同虚设。最常见三类雷:

this
:外部代码可能也锁你实例,引发意外阻塞
typeof(MyClass)
或字符串字面量:跨 Assembly 或 intern 字符串共享,锁范围远超预期
锁 public 字段或可变对象(比如
public object SyncRoot
):别人改了它,你的
Monitor.Exit
就找不到原锁对象

正确做法永远是:

private readonly object _syncObj = new object();
—— 它只属于你,不可变,不暴露。

Monitor 提供能力,lock 提供安全底线;该用哪个,不看“高级感”,而看有没有 Wait/Pulse 或超时需求。其余时候,老老实实写

lock
,少一行代码,少一个 bug。

相关推荐