我有以下设计模式:
var myObjectWithEvents = new ObjectWithEvents();
using (var mre = new ManualResetEvent(false)) {
var onEvent = new EventHandler<EventArgs>((sender, e) => { mre.Set(); });
try {
myObjectWithEvents.OnEvent += onEvent;
var task = Task.Factory.StartNew(() => {
myObjectWithEvents.DoSomethingThatShouldRaiseAnEvent();
});
var timedOut = !mre.WaitOne(10000);
}
finally {
myObjectWithEvents.OnEvent -= onEvent;
}
}
我的问题是,如果在WaitOne
超时并且执行步骤超出正在使用的块之后引发OnEvent
,则本地onEvent
事件处理程序仍将被调用,并尝试设置已经被释放的ManualResetEvent mre
,即使onEvent
应该已经从OnEvent
中注销。
一个简单的解决方法是检查mre
是否已经被处理,但不幸的是,没有这样的字段,而且我认为将mre.Set()
封装在try-catch块中以忽略异常是不干净的,因为异常可能非常频繁地发生。
您认为在不遇到此类问题的情况下,实现上述代码模式的目的(即等待引发事件)的最佳和最简单的方法是什么?
编辑:感谢您的回答,我创建了以下扩展,并将mre.Set()
替换为mre.TrySet()
:
public static void TrySet(this ManualResetEvent mre) {
if (!mre.SafeWaitHandle.IsClosed) mre.Set();
}
ManualResetEvent.SafeWaitHandle.IsClosed
看起来很奇怪,但dispose所做的唯一一件事就是关闭safeHandler,这是它的dispose打算…的唯一对象
SafeWaitHandle的Dispose将此属性从False更改为True。
您可以尝试通过mre进行检查。SafeWaitHandle.Is关闭属性
作为一个案例,尝试使用简单的布尔开关来指示手动重置事件是否为实际设置:
bool isMreSync = true;
var myObjectWithEvents = new ObjectWithEvents();
using (var mre = new ManualResetEvent(false))
{
var onEvent = new EventHandler<EventArgs>((sender, e) =>
{
if (isMreSync)
{
mre.Set();
}
});
// try ... finally block
}
isMreSync = false;
若事件可能异步执行,则同步对布尔开关的访问。