C# 是否需要对ManualResetEvent调用Close()?
我一直在阅读.NET线程,正在编写一些使用。我在网上找到了很多代码示例。但是,在阅读的文档中,我看到了以下内容: WaitHandle实现Dispose 图案请参见实施和最终确定 Dispose以清理未托管的 资源C# 是否需要对ManualResetEvent调用Close()?,c#,multithreading,dispose,waithandle,resource-leak,C#,Multithreading,Dispose,Waithandle,Resource Leak,我一直在阅读.NET线程,正在编写一些使用。我在网上找到了很多代码示例。但是,在阅读的文档中,我看到了以下内容: WaitHandle实现Dispose 图案请参见实施和最终确定 Dispose以清理未托管的 资源 所有示例似乎都没有对它们创建的ManualResetEvent对象调用.Close(),甚至pfxteam博客上的一篇文章(Edit-这有一个使用块我错过了)。这只是一个例子,还是不需要?我很好奇,因为WaitHandle“封装了操作系统特定的对象”,所以很容易发生资源泄漏。关闭是在
所有示例似乎都没有对它们创建的ManualResetEvent对象调用.Close(),甚至pfxteam博客上的一篇文章(Edit-这有一个使用块我错过了)。这只是一个例子,还是不需要?我很好奇,因为WaitHandle“封装了操作系统特定的对象”,所以很容易发生资源泄漏。关闭是在ManualResetEvent的Dispose中处理的,这由'using'语句调用
您会注意到代码
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”关键字。即使代码抛出异常,这也会在完成时自动调用dispose方法。通常,如果对象实现了
IDisposable
,那么它这样做是有原因的,您应该调用dispose
(或关闭
,视情况而定)。在您站点的示例中,ManualResetEvent被包装在使用语句的中,该语句将“自动”处理调用Dispose
。在这种情况下,Close
与Dispose
同义(这在大多数提供Close
方法的IDisposable
实现中都是如此)
示例中的代码如下所示:
using (var mre = new ManualResetEvent(false))
{
...
}
扩展到
var mre = new ManualResetEvent(false);
try
{
...
}
finally
{
((IDispoable)mre).Dispose();
}
我经常使用ManualResetEvent
,我认为我从未在单个方法中使用过它——它始终是类的实例字段。因此,使用()
通常不适用
如果类实例字段是ManualResetEvent
的实例,请使类实现IDisposable
并在Dispose()
方法调用ManualResetEvent.Close()
。然后,在类的所有用法中,您都需要使用using()
或使包含的类实现IDisposable
并重复,然后重复…如果您使用的是带有匿名方法的ManualResetEvent
,那么它显然很有用。但正如山姆提到的,它们通常可以传递给工人,然后设置和关闭
所以我想说,这取决于您如何使用它的上下文-代码示例有一个很好的例子来说明我的意思
下面是一个基于MSDN示例的示例,说明如何使用
语句创建带有的WaitHandles:
系统.ObjectDisposedException
“安全手柄已关闭”
const int threads=25;
void ManualWaitHandle()
{
ManualResetEvent[]manualEvents=新的ManualResetEvent[螺纹];
对于(int i=0;i
我最近收到了Joseph Albahari,Ben Albahari的一段摘录。在第834页的第21章:线程中有一节讨论了这一点
处理等待句柄
一旦你完成了等待
句柄,您可以调用它的Close方法
要释放操作系统
资源。或者,你可以
只需删除对wait的所有引用
处理并允许垃圾收集器
以后再为你做这项工作
(等待句柄执行处置
终结器调用的模式
关闭)。这是为数不多的一个
依赖此备份的场景
(可以说)是可以接受的,因为等待
句柄的操作系统负担很轻
(异步代理依赖于
正是这一机制的释放
它们的IAsyncResult的等待句柄)
等待句柄被释放
当应用程序
域卸载
在查看代码时,我完全忽略了使用块。感谢您指出。这似乎是一个在ManualResetEvent上不调用.Close()的示例,并且没有using块。我不认为worker可以在set之后关闭它,因为主线程正在WaitHandle.WaitAll(manualEvents)调用中使用它。@Kevin我的观点是WaitHandles数组在创建时不能包装在using子句中,因为我认为它们到达时会被关闭,不过我需要检查一下。WaitHandle.Finalize的文档说不再有来自.NET2.0的实现。您还可以通过反编译器看到这一点。WaitHandle不再具有终结器。我不知道为什么,但任何被遗弃的侍者把手似乎都会漏水。请看@Djof:尽管WaitHandle
不再有Finalize
方法,但我认为这并不意味着它们会泄漏。相反,清理是在SafeHandle
中处理的,WaitHandle
对其进行引用。
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;
}
}