Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't Mutex get released when disposed?

Tags:

c#

.net

mutex

I have the following code:

using (Mutex mut = new Mutex(false, MUTEX_NAME)) {     if (mut.WaitOne(new TimeSpan(0, 0, 30)))     {        // Some code that deals with a specific TCP port        // Don't want this to run at the same time in another process     } } 

I've set a breakpoint within the if block, and ran the same code within another instance of Visual Studio. As expected, the .WaitOne call blocks. However, to my surprise, as soon as I continue in the first instance and the using block terminates, I get an exception in the second process about an abandoned Mutex.

The fix is to call ReleaseMutex:

using (Mutex mut = new Mutex(false, MUTEX_NAME)) {     if (mut.WaitOne(new TimeSpan(0, 0, 30)))     {        // Some code that deals with a specific TCP port        // Don't want this to run twice in multiple processes     }      mut.ReleaseMutex(); } 

Now, things work as expected.

My Question: Usually the point of an IDisposable is it cleans up whatever state you put things in. I could see perhaps having multiple waits and releases within a using block, but when the handle to the Mutex is disposed, shouldn't it get released automatically? In other words, why do I need to call ReleaseMutex if I'm in a using block?

I'm also now concerned that if the code within the if block crashes, I'll have abandoned mutexes lying around.

Is there any benefit to putting Mutex in a using block? Or, should I just new up a Mutex instance, wrap it in a try/catch, and call ReleaseMutex() within the finally block (Basically implementing exactly what I thought Dispose() would do)

like image 248
Mike Christensen Avatar asked Aug 21 '14 17:08

Mike Christensen


People also ask

Should I dispose mutex?

A mutex is a Windows kernel object (here wrapped in a . NET object). As such, it is an unmanaged resource that should be disposed.

Can you reuse a mutex?

A mutex must be created once. Calling the pthread_mutex_init subroutine more than once with the same mutex parameter (for example, in two threads concurrently executing the same code) should be avoided. The second call will fail, returning an EBUSY error code.

How do you release mutex?

The thread that creates the mutex does not own it initially. The ReleaseMutex method is used to release the mutex when it is no longer needed. // This example shows how a Mutex is used to synchronize access // to a protected resource. Unlike Monitor, Mutex can be used with // WaitHandle.

What does locking a mutex do?

Locks a mutex object, which identifies a mutex. Mutexes are used to protect shared resources. If the mutex is already locked by another thread, the thread waits for the mutex to become available. The thread that has locked a mutex becomes its current owner and remains the owner until the same thread has unlocked it.


Video Answer


1 Answers

The documentation explains (in the "Remarks" section) that there is a conceptual difference between instantiating a Mutex object (which does not, in fact, do anything special as far as synchronization goes) and acquiring a Mutex (using WaitOne). Note that:

  • WaitOne returns a boolean, meaning that acquiring a Mutex can fail (timeout) and both cases must be handled
  • When WaitOne returns true, then the calling thread has acquired the Mutex and must call ReleaseMutex, or else the Mutex will become abandoned
  • When it returns false, then the calling thread must not call ReleaseMutex

So, there's more to Mutexes than instantiation. As for whether you should use using anyway, let's take a look at what Dispose does (as inherited from WaitHandle):

protected virtual void Dispose(bool explicitDisposing) {     if (this.safeWaitHandle != null)     {         this.safeWaitHandle.Close();     } } 

As we can see, the Mutex is not released, but there is some cleanup involved, so sticking with using would be a good approach.

As to how you should proceed, you can of course use a try/finally block to make sure that, if the Mutex is acquired, that it gets properly released. This is likely the most straightforward approach.

If you really don't care about the case where the Mutex fails to be acquired (which you haven't indicated, since you pass a TimeSpan to WaitOne), you could wrap Mutex in your own class that implements IDisposable, acquire the Mutex in the constructor (using WaitOne() with no arguments), and release it inside Dispose. Although, I probably wouldn't recommend this, as this would cause your threads to wait indefinitely if something goes wrong, and regardless there are good reasons for explicitly handling both cases when attempting an acquire, as mentioned by @HansPassant.

like image 51
voithos Avatar answered Sep 24 '22 17:09

voithos