C# 是否需要对ManualResetEvent调用Close()?

C# 是否需要对ManualResetEvent调用Close()?,c#,multithreading,dispose,waithandle,resource-leak,C#,Multithreading,Dispose,Waithandle,Resource Leak,我一直在阅读.NET线程,正在编写一些使用。我在网上找到了很多代码示例。但是,在阅读的文档中,我看到了以下内容: WaitHandle实现Dispose 图案请参见实施和最终确定 Dispose以清理未托管的 资源 所有示例似乎都没有对它们创建的ManualResetEvent对象调用.Close(),甚至pfxteam博客上的一篇文章(Edit-这有一个使用块我错过了)。这只是一个例子,还是不需要?我很好奇,因为WaitHandle“封装了操作系统特定的对象”,所以很容易发生资源泄漏。关闭是在

我一直在阅读.NET线程,正在编写一些使用。我在网上找到了很多代码示例。但是,在阅读的文档中,我看到了以下内容:

WaitHandle实现Dispose 图案请参见实施和最终确定 Dispose以清理未托管的 资源


所有示例似乎都没有对它们创建的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;
    }
}