manualreseteventslim 是轻量级手动重置事件,非信号量;应使用 wait() 而非 waitone();初始状态通常为 false;set() 后需显式 reset() 以维持同步逻辑;必须调用 dispose() 防资源泄漏。

ManualResetEventSlim 不是“信号量”,它和 Semaphore 或 SemaphoreSlim 完全不同类——它是轻量级的“手动重置事件”,作用是线程间发一次“开闸”信号,让所有等待者同时通行,且必须手动关闸。
Wait() 和 WaitOne() 到底该用哪个?
别混淆:ManualResetEventSlim 没有 WaitOne() 方法,只有 Wait();而 ManualResetEvent 才用 WaitOne()。混用会编译报错 'ManualResetEventSlim' does not contain a definition for 'WaitOne'。
-
ManualResetEventSlim.Wait():支持超时、取消令牌(CancellationToken),性能更好,推荐在 .NET 4.0+ 环境中优先使用 -
ManualResetEvent.WaitOne():老式 API,底层调用内核对象,开销大;仅当需跨进程同步或兼容旧系统时才考虑 - 若你正从
ManualResetEvent迁移,记得把所有.WaitOne(1000)改成.Wait(1000),否则直接报错
为什么 new ManualResetEventSlim(false) 是最常见写法?
因为绝大多数场景下,你希望线程先“停住等指令”,而不是一启动就直接冲过去。初始状态设为 false(未触发)才是安全默认值。
-
new ManualResetEventSlim(true):相当于门一开始就是开着的,Wait()立即返回,适合做“预热就绪”标记(如服务已加载完成) - 误写成
true又没留意逻辑,会导致等待逻辑被跳过,出现“本该阻塞却没阻塞”的隐蔽 bug - WPF 关闭前等待后台线程收尾这类场景,必须用
false初始化,否则Wait()不生效,程序提前退出丢数据
Set() 之后不 Reset() 会发生什么?
Set() 是“开闸”,不是“按一下就开一秒”。它把内部状态永久设为 true,直到你显式调用 Reset() —— 这正是 “Manual” 的含义。
- 忘记
Reset():后续所有Wait()调用都会立即返回,同步逻辑彻底失效 - 多轮控制(比如开门→干活→关门→再开门)必须严格配对:
Set()→Reset()→Set()→Reset() -
IsSet属性可读取当前状态,调试时建议加日志:Console.WriteLine($"Gate is open: {_mres.IsSet}");
Dispose() 不只是“好习惯”,而是必须做
ManualResetEventSlim 内部可能持有轻量同步资源(如自旋锁 + 内核回退机制),不 Dispose() 在长期运行服务中会缓慢泄漏资源。
- 必须在所有线程都结束对该实例的访问后调用
Dispose() - 典型模式:用
using不适用(因需跨线程共享),应在协调线程确认所有工作线程退出后再_mres.Dispose() - 已
Dispose()的实例再调用Wait()或Set()会抛ObjectDisposedException,注意捕获或避免重复释放
真正容易被忽略的是状态生命周期管理:它不像 lock 那样自动进出,Set() 和 Reset() 的时机必须和业务阶段严格对齐,少一次 Reset() 就等于整套同步逻辑“失锁”。











