Do I need to call Close() on a ManualResetEvent?
Asked Answered
A

6

16

I've been reading up on .NET Threading and was working on some code that uses a ManualResetEvent. I have found lots of code samples on the internet. However, when reading the documentation for WaitHandle, I saw the following:

WaitHandle implements the Dispose pattern. See Implementing Finalize and Dispose to Clean Up Unmanaged Resources.

None of the samples seem to call .Close() on the ManualResetEvent objects they create, even the nice Recursion and Concurrency article from the pfxteam blog (Edit - this has a using block I has missed). Is this just example oversight, or not needed? I am curious because a WaitHandle "encapsulates operating system–specific objects," so there could easily be a resource leak.

Alonzoaloof answered 10/2, 2010 at 2:58 Comment(0)
L
11

In general, if an object implements IDisposable it is doing so for a reason and you should call Dispose (or Close, as the case may be). In the example you site, the ManualResetEvent is wrapped inside a using statement, which will "automatically" handle calling Dispose. In this case, Close is synonymous with Dispose (which is true in most IDisposable implementations that provide a Close method).

The code from the example:

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

expands to

var mre = new ManualResetEvent(false);
try
{
   ...
}
finally
{
   ((IDispoable)mre).Dispose();
}
Lozengy answered 10/2, 2010 at 3:4 Comment(0)
A
22

I was recently forwarded an excerpt from C# 4.0 in a Nutshell: The Definitive Reference By Joseph Albahari, Ben Albahari. On page 834, in Chapter 21: Threading there is a section talking about this.

Disposing Wait Handles

Once you’ve finished with a wait handle, you can call its Close method to release the operating system resource. Alternatively, you can simply drop all references to the wait handle and allow the garbage collector to do the job for you sometime later (wait handles implement the disposal pattern whereby the finalizer calls Close). This is one of the few scenarios where relying on this backup is (arguably) acceptable, because wait handles have a light OS burden (asynchronous delegates rely on exactly this mechanism to release their IAsyncResult’s wait handle).

Wait handles are released automatically when an application domain unloads.

Alonzoaloof answered 1/3, 2010 at 18:44 Comment(2)
The documentation for WaitHandle.Finalize says there is no longer an implementation from .NET 2.0. You can also see this with a decompiler. WaitHandle no longer has a finalizer. I don't know why, but any abandoned WaitHandle will leak it seems. See msdn.microsoft.com/en-us/library/vstudio/bb291974(v=vs.90).aspxTableland
@Djof: Even though WaitHandle no longer has a Finalize method, I don't think that means they leak. Instead, cleanup is handled within a SafeHandle to which a WaitHandle holds a reference.Guidry
L
11

In general, if an object implements IDisposable it is doing so for a reason and you should call Dispose (or Close, as the case may be). In the example you site, the ManualResetEvent is wrapped inside a using statement, which will "automatically" handle calling Dispose. In this case, Close is synonymous with Dispose (which is true in most IDisposable implementations that provide a Close method).

The code from the example:

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

expands to

var mre = new ManualResetEvent(false);
try
{
   ...
}
finally
{
   ((IDispoable)mre).Dispose();
}
Lozengy answered 10/2, 2010 at 3:4 Comment(0)
L
2

The Close is handled inside ManualResetEvent's Dispose, and that's called by the 'using' statement.

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

Lieb answered 10/2, 2010 at 3:1 Comment(0)
E
2

You'll notice the code

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

uses the 'using' keyword. This automatically calls the dispose method when finished even if the code throws an exception.

Endicott answered 10/2, 2010 at 3:3 Comment(1)
I totally missed the using block when looking over that code. Thanks for pointing it out.Alonzoaloof
L
2

I've used ManualResetEvent a lot and don't think I've ever used it inside a single method--it's always an instance field of a class. Therefore using() often does not apply.

If you have a class instance field that is an instance of ManualResetEvent, make your class implement IDisposable and in your Dispose() method call ManualResetEvent.Close(). Then in all usages of your class, you need to use using() or make the containing class implement IDisposable and repeat, and repeat...

Logotype answered 10/2, 2010 at 3:44 Comment(0)
M
2

If you're using a ManualResetEvent with anonymous methods then it's obviously useful. But as Sam mentioned they can often be passed around into workers, and then set and closed.

So I would say it depends on the context of how you are using it - the MSDN WaitHandle.WaitAll() code sample has a good example of what I mean.

Here's an example based on the MSDN sample of how creating the WaitHandles with a using statement would exception:

System.ObjectDisposedException
"Safe handle has been closed"

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;
    }
}
Mountaineer answered 10/2, 2010 at 11:1 Comment(2)
That appears to be an example where .Close() is not called on the ManualResetEvent, and there is no using block. I don't think the worker can close it after a set because the main thread is using it in the WaitHandle.WaitAll(manualEvents) call.Alonzoaloof
@Kevin my point was the array of WaitHandles can't be wrapped in an using clause when created as I think they'd be closed when they arrived, I'd need to check though.Mountaineer

© 2022 - 2024 — McMap. All rights reserved.