【问题标题】:How to check if ManualResetEvent has been disposed, when trying to Set() it inside an EventHandler?尝试在 EventHandler 中设置()它时,如何检查 ManualResetEvent 是否已被处理?
【发布时间】: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


【解决方案1】:
ManualResetEvent.SafeWaitHandle.IsClosed

看起来很奇怪,但是 dispose 所做的唯一一件事就是关闭 safeHandler,这是 dispose 想要的唯一对象...

SafeWaitHandle 的 Dispose,将此属性从 False 更改为 True。

【讨论】:

    【解决方案2】:

    您可以尝试通过 mre.SafeWaitHandle.IsClosed 属性检查它

    【讨论】:

      【解决方案3】:

      作为一个案例尝试使用简单的布尔开关来指示设置manualResetEvent是否是实际的:

      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;
      

      如果事件可能异步执行 - 同步访问布尔开关。

      【讨论】:

      • 我认为这个解决方案不是很安全,可能会在检查之后和调用Set 之前将isMreSync 设置为false(甚至丢弃),因为没有额外的锁定.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多