c# lock 和 Monitor.TryEnter 的区别

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

lock 会一直等,Monitor.TryEnter 可设超时

这是最直接的区别:当你用

lock(obj)
,线程一旦抢不到锁,就会**无限阻塞**,直到前一个持有者释放;而
Monitor.TryEnter(obj, timeout)
允许你指定最多等多久(毫秒),超时就返回
false
,不卡死。

适合对响应时间敏感的场景,比如 UI 线程、实时服务接口,不能因锁被长期占用而挂起
timeout = 0
表示“只试一次”,立即返回成功或失败,常用于非阻塞轮询
timeout = -1
等价于无限等待 —— 此时行为接近
lock
,但依然要手动配对
Exit
,没
lock
安全

lock 自动保底,Monitor.TryEnter 必须自己写 finally 释放

lock
是语法糖,编译后自动包成
try-finally
,哪怕临界区抛异常,锁也一定释放;
Monitor.TryEnter
不带这种保障 —— 如果你忘了在
finally
里调用
Monitor.Exit
,或者只在
if
分支里调了,就可能造成死锁。

正确写法必须是:
bool lockTaken = false; try { if (Monitor.TryEnter(obj, 1000)) lockTaken = true; /* 临界区 */ } finally { if (lockTaken) Monitor.Exit(obj); }
漏掉
lockTaken
判断?
Monitor.Exit
会抛
SynchronizationLockException
Monitor.Exit
写在
if
里而不是
finally
?异常一出,锁就永远拿不回来了

Monitor.TryEnter 能配合 Wait/Pulse 实现复杂同步,lock 不行

如果你需要做生产者-消费者、信号量式唤醒、条件等待(比如“等队列不为空再取”),就必须用

Monitor.Wait
Monitor.Pulse
—— 这些方法只能在已持有 Monitor 锁的前提下调用,而
lock
语法不暴露锁状态,也没法中途释放再等待。

Monitor.Wait(obj)
会**自动释放锁 + 挂起当前线程**,等别人
Pulse
后重新争锁
这个“释放锁→等待→重获锁”的原子过程,
lock
无法表达
所以涉及线程协作逻辑时,哪怕只是加个超时,也得切到
Monitor.Enter/TryEnter + Wait/Pulse
全套流程

锁对象安全要求一样,但 Monitor 更容易误用

两者都要求锁对象是私有、引用类型、不可变(推荐

private readonly object _sync = new object();
);但
Monitor
因为方法多、路径长,更容易踩坑:

对同一个对象反复
Enter
Exit
次数不够?锁不会真正释放
在没持锁时调
Monitor.Wait
Monitor.Pulse
?直接抛
SynchronizationLockException
用字符串字面量或
this
当锁对象?跨实例/跨程序集意外共享,引发隐蔽竞争
Monitor.IsEntered(obj)
是调试用的,**不能用来代替 TryEnter 做锁检查**(它不保证原子性)

简单场景无脑用

lock
;只要出现“等太久不行”“我要等某个条件”“我得通知别人”,就得切到
Monitor
,并且每一步都要对齐 Enter/Exit、Wait/Pulse 的契约 —— 少一个
finally
,就少一分确定性。

相关推荐

热文推荐