【发布时间】:2012-01-25 14:23:03
【问题描述】:
我有以下设计模式:
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 并且执行步骤超出 using 块,则仍将调用本地 onEvent 事件处理程序并尝试设置 ManualResetEvent mre 这将即使 onEvent 应该已从 OnEvent 注销。
一个简单的解决方法是检查 mre 是否已经被释放,但不幸的是没有这样的字段,我相信将 mre.Set() 包装在 try catch 块中以忽略异常是不干净的,因为异常可能会经常发生。
您认为在不遇到此类问题的情况下实现上述 Code Pattern 目的(即等待引发事件)的最佳和最简单的方法是什么?
编辑:感谢您的回答,我创建了以下扩展并将mre.Set() 替换为mre.TrySet():
public static void TrySet(this ManualResetEvent mre) {
if (!mre.SafeWaitHandle.IsClosed) mre.Set();
}
【问题讨论】:
-
您能否详细说明您的申请要求?您要求“实现上述 Code Pattern 目的的最佳和最简单的方法”,但这让我们根据您当前的代码猜测您的需求是什么。
-
在处置的 MRE 上执行 Set() 是否会导致任何问题?
-
是的 mre.Set() 在已处理的 mre 上会抛出异常...
-
但是,请注意,上面的“答案”代码并不完全正确,因为它表现出一种竞争条件,其中“mre.SafeWaitHandle.IsClosed”可以在您检查之后变为真,但在 mre 之前.Set() 已到达。我认为毫无意义的异常抛出行为是 .NET 中的设计缺陷,因为我看不到解决这种竞争条件的正确方法。
标签: c# .net multithreading design-patterns dispose