Mutex不是lock的替代品,仅跨进程同步时使用;单进程应优先用lock,因Mutex性能差、易出错且需严格配对ReleaseMutex。

Mutex 不是 lock 的替代品,跨进程才用它;单进程里硬套 Mutex,性能差、易出错、还容易漏释放。
命名 Mutex 才能跨进程,Local 和 Global 前缀不能乱写
无参 new Mutex() 创建的是匿名互斥体,只在当前进程内有效——这跟 lock 没本质区别,但开销更大。真要跨进程,必须用带名称的构造函数:
-
"Local\myapp-config-writer":默认作用域,同一用户会话(Session)内所有进程可见,普通桌面应用够用 -
"Global\myapp-config-writer":全系统可见,但非管理员进程创建时会抛UnauthorizedAccessException,除非服务以 LocalSystem 运行并显式配置 DACL - 名称区分大小写,建议全小写 + 连字符,避免特殊字符和空格
- 不要依赖
out createdNew判断“谁先拿到锁”——它只说明内核对象是否新建,不反映当前线程是否获得所有权
WaitOne 超时或抛异常,两种失败必须分开处理
WaitOne 返回 false 只代表超时,不是最危险的情况;真正要警惕的是 AbandonedMutexException:
- 该异常意味着上一个持有锁的进程崩溃或未调用
ReleaseMutex(),临界区资源可能已损坏 - 捕获该异常后,当前线程自动获得所有权,但你必须手动校验共享状态(比如读取文件后验证 JSON 格式、比对 CRC、重载配置缓存)
-
WaitOne(0)是非阻塞试探,适合轮询;WaitOne(Timeout.Infinite)会永久挂起,生产环境慎用 - 别在
catch块里直接ReleaseMutex()——异常发生时你未必持有锁,调用会抛ApplicationException
必须用 try/finally 保证 ReleaseMutex,且不能跨线程释放
Mutex 没有 using 语法糖保障释放,忘记 ReleaseMutex() 就等于给其他进程设了永久路障:
- 所有临界区操作必须包在
try块里,ReleaseMutex()必须放在对应的finally中 -
ReleaseMutex()只能由获取锁的同一线程调用,A 线程WaitOne成功后,B 线程调用会立即抛ApplicationException - 不推荐在
Dispose()或Close()后继续使用该实例;using块可自动调用Close(),但不能替代ReleaseMutex()的语义 - 如果临界区逻辑可能 throw,又不想在 finally 里重复判断是否持有锁,可用
bool hasOwnership = mutex.WaitOne(...)+if (hasOwnership) mutex.ReleaseMutex();
单进程场景下,优先用 lock,别为“看起来高级”选 Mutex
很多人误以为 Mutex 更“底层”或“更安全”,其实恰恰相反:
-
lock(obj)是用户态轻量级同步,几乎无额外开销;Mutex.WaitOne()每次都触发内核态切换,实测慢 10 倍以上 -
Monitor、SpinLock、ReaderWriterLockSlim都是进程内专用,设计目标就是高效;Mutex的唯一不可替代性在于跨进程 - 若只是防止程序多开,
createdNew输出参数配合命名 Mutex 是可行方案,但注意它不防“同一进程多次启动线程抢锁”,仅防多进程实例 - 真需要跨 Session(如 Windows 服务和用户桌面进程通信),光靠
Global往往不够,得搭配命名管道、内存映射文件或注册表标记做兜底
最容易被忽略的一点:Mutex 的“放弃检测”是 Windows 内核行为,.NET 只是暴露了这个信号。一旦看到 AbandonedMutexException,不要只想着“赶紧抢锁干活”,先停一下——那块共享资源,很可能已经不是你想象的样子了。


















