【问题标题】:Do I need to call Close() on a ManualResetEvent?我需要在 ManualResetEvent 上调用 Close() 吗?
【发布时间】:2010-02-10 02:58:55
【问题描述】:

我一直在阅读 .NET 线程,并且正在研究一些使用 ManualResetEvent 的代码。我在互联网上找到了很多代码示例。但是,在阅读WaitHandle 的文档时,我看到了以下内容:

WaitHandle 实现 Dispose 图案。请参阅实现 Finalize 和 处置以清理非托管 资源。

似乎没有一个示例在他们创建的 ManualResetEvent 对象上调用 .Close(),即使是来自 pfxteam 博客的精彩 Recursion and Concurrency 文章编辑 - 这有一个我错过的 using 块)。这只是示例监督,还是不需要?我很好奇,因为 WaitHandle “封装了操作系统特定的对象”,因此很容易发生资源泄漏。

【问题讨论】:

    标签: c# multithreading dispose waithandle resource-leak


    【解决方案1】:

    我最近收到了约瑟夫·阿尔巴哈里 (Joseph Albahari)、本·阿尔巴哈里 (Ben Albahari) 的 C# 4.0 in a Nutshell: The Definitive Reference 的摘录。在第 834 页,在 Chapter 21: Threading 中有一节讨论了这一点。

    释放等待句柄

    等待完成后 句柄,你可以调用它的Close方法 释放操作系统 资源。或者,您可以 只需删除对等待的所有引用 处理并允许垃圾收集器 稍后为你做这项工作 (等待处理实施处置 终结器调用的模式 关闭)。这是少数几个之一 依赖此备份的场景 是(可以说)可以接受的,因为等待 句柄的操作系统负担较轻 (异步委托依赖于 正是这种机制来释放 他们的 IAsyncResult 的等待句柄)。

    等待句柄被释放 申请时自动 域卸载。

    【讨论】:

    • WaitHandle.Finalize 的文档说不再有 .NET 2.0 的实现。您也可以使用反编译器看到这一点。 WaitHandle 不再有终结器。我不知道为什么,但任何废弃的 WaitHandle 似乎都会泄漏。见msdn.microsoft.com/en-us/library/vstudio/bb291974(v=vs.90).aspx
    • @Djof:尽管WaitHandle 不再有Finalize 方法,但我认为这并不意味着它们会泄漏。相反,清理是在 SafeHandle 中处理的,WaitHandle 持有对它的引用。
    【解决方案2】:

    一般来说,如果一个对象实现了IDisposable,它这样做是有原因的,你应该调用Dispose(或Close,视情况而定)。在您站点的示例中,ManualResetEvent 包含在 using 语句中,该语句将“自动”处理调用 Dispose。在这种情况下,CloseDispose 的同义词(在大多数提供 Close 方法的 IDisposable 实现中都是如此)。

    示例代码:

    using (var mre = new ManualResetEvent(false))
    {
       ...
    }
    

    扩展到

    var mre = new ManualResetEvent(false);
    try
    {
       ...
    }
    finally
    {
       ((IDispoable)mre).Dispose();
    }
    

    【讨论】:

      【解决方案3】:

      Close 在 ManualResetEvent 的 Dispose 内处理,并由“using”语句调用。

      http://msdn.microsoft.com/en-us/library/yh598w02%28VS.100%29.aspx

      【讨论】:

        【解决方案4】:

        你会注意到代码

         using (var mre = new ManualResetEvent(false))
         {
            // Process the left child asynchronously
            ThreadPool.QueueUserWorkItem(delegate
            {
                Process(tree.Left, action);
                mre.Set();
            });
        
            // Process current node and right child synchronously
            action(tree.Data);
            Process(tree.Right, action);
        
            // Wait for the left child
            mre.WaitOne();
        }
        

        使用“使用”关键字。即使代码抛出异常,这也会在完成时自动调用 dispose 方法。

        【讨论】:

        • 在查看该代码时,我完全错过了 using 块。感谢您指出。
        【解决方案5】:

        我经常使用ManualResetEvent,但我认为我从未在单个方法中使用过它——它始终是类的实例字段。因此using() 通常不适用。

        如果您有一个类实例字段是ManualResetEvent 的实例,请让您的类实现IDisposable 并在您的Dispose() 方法中调用ManualResetEvent.Close()。然后在您的类的所有用法中,您需要使用using() 或使包含类实现IDisposable 并重复,再重复...

        【讨论】:

          【解决方案6】:

          如果您将ManualResetEvent 与匿名方法一起使用,那么它显然很有用。但正如 Sam 提到的,它们通常可以传递给工人,然后设置和关闭。

          所以我会说这取决于您如何使用它的上下文 - the MSDN WaitHandle.WaitAll() 代码示例很好地说明了我的意思。

          这是一个基于 MSDN 示例的示例,说明如何使用 using 语句创建 WaitHandles 会出现异常:

          System.ObjectDisposedException
          "安全手柄已关闭"

          const int threads = 25;
          
          void ManualWaitHandle()
          {
              ManualResetEvent[] manualEvents = new ManualResetEvent[threads];
          
              for (int i = 0; i < threads; i++)
              {
                  using (ManualResetEvent manualResetEvent = new ManualResetEvent(false))
                  {
                      ThreadPool.QueueUserWorkItem(new WaitCallback(ManualWaitHandleThread), new FileState("filename", manualResetEvent));
                      manualEvents[i] = manualResetEvent;
                  }
              }
          
              WaitHandle.WaitAll(manualEvents);
          }
          
          void ManualWaitHandleThread(object state)
          {
              FileState filestate = (FileState) state; 
              Thread.Sleep(100);
              filestate.ManualEvent.Set();
          }
          
          class FileState
          {
              public string Filename { get;set; }
              public ManualResetEvent ManualEvent { get; set; }
          
              public FileState(string fileName, ManualResetEvent manualEvent)
              {
                  Filename = fileName;
                  ManualEvent = manualEvent;
              }
          }
          

          【讨论】:

          • 这似乎是一个示例,其中没有在 ManualResetEvent 上调用 .Close(),并且没有 using 块。我不认为工作人员可以在设置后关闭它,因为主线程正在 WaitHandle.WaitAll(manualEvents) 调用中使用它。
          • @Kevin 我的意思是,WaitHandles 的数组在创建时不能包含在 using 子句中,我认为它们到达时会被关闭,我会不过需要检查。
          猜你喜欢
          • 1970-01-01
          • 2010-10-14
          • 2015-01-08
          • 1970-01-01
          • 2014-09-26
          • 2015-10-14
          • 1970-01-01
          • 1970-01-01
          • 2012-08-11
          相关资源
          最近更新 更多