我需要在ManualResetEvent上调用Close()吗?

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

WaitHandle实现了Dispose模式。 请参阅实现Finalize和Dispose以清理非托管资源。

没有任何样本似乎在他们创建的ManualResetEvent对象上调用.Close(), 甚至是来自pfxteam博客的好的Recursion and Concurrency文章编辑 – 这有一个我错过的使用块)。 这只是示例疏忽,还是不需要? 我很好奇,因为WaitHandle“封装了特定于操作系统的对象”,因此很容易出现资源泄漏。

通常,如果一个对象实现了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(); } 

我最近转发了一篇摘自C#4.0的Nutshell:The Definitive Reference作者:Joseph Albahari,Ben Albahari。 在页834,在第21章:线程中有一节讨论这个。

处理等待句柄

完成等待句柄后,可以调用其Close方法释放操作系统资源。 或者,您可以简单地删除对等待句柄的所有引用,并允许垃圾收集器稍后为您执行该作业(等待句柄实现处理模式,由此终结器调用Close )。 这是依赖此备份(可以说)可接受的少数场景之一,因为等待句柄具有轻微的操作系统负担(异步委托完全依赖于此机制来释放其IAsyncResult的等待句柄)。

应用程序域卸载时会自动释放等待句柄。

Close在ManualResetEvent的Dispose中处理,并由’using’语句调用。

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

你会注意到代码

  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(); } 

使用’using’关键字。 即使代码抛出exception,这也会在完成后自动调用dispose方法。

我经常使用ManualResetEvent并且不认为我曾经在单个方法中使用过它 – 它总是一个类的实例字段。 因此, using()通常不适用。

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

如果您使用带有匿名方法的ManualResetEvent ,那么它显然很有用。 但正如萨姆所说,他们经常可以传递给工人,然后设置和关闭。

所以我会说这取决于你如何使用它的上下文 – MSDN WaitHandle.WaitAll()代码示例有一个很好的例子我的意思。

下面是一个基于MSDN示例的示例,该示例说明如何using语句创建WaitHandles将是exception:

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; } }