Mutex

This lesson explains how the Mutex class in C# works and its related caveats.

Mutex

A Mutex object can be acquired by a thread to exercise mutual exclusion. Protected resources or critical sections can be guarded by a Mutex object to allow a single thread to access a protected resource or execute within a critical section.

The most important characteristic of the Mutex class is that it enforces thread identity. A mutex can be released only by the thread that acquired it. By contrast, the Semaphore class does not enforce thread identity. A mutex can also be passed across application domain boundaries and used for interprocess synchronization.

Ping Pong Example

As an example, we'll write a program that prints "Ping" and "Pong" in strict alternation on the console. We'll create two threads that take turns to print on the console. Let's discuss the ping() method first which writes the "Ping" string on the console. The first question, we may ask is how to notify the two threads of whose turn it is to print on the console. We can use a boolean variable flag to denote which thread's turn it is to write. Once a thread has taken its turn, it'll flip the boolean variable to let the other thread takes its turn.

Since the state is shared and mutated by the two threads, we must let only one thread read or write it at any time. Additionally, each thread must check for the flag variable to flip in a busy while loop. In later chapters, we'll learn how to avoid busy waiting. The implementation for the ping() is as follows:

Press + to interact
void ping()
{
while (true)
{
mutex.WaitOne();
while (flag)
{
// Release the mutex so that another
// thread gets a chance to acquire it
mutex.ReleaseMutex();
// Acquire the mutex before checking
// the loop condition
mutex.WaitOne();
}
Console.WriteLine("Ping");
flag = true;
mutex.ReleaseMutex();
}
}

Pay special attention to the while loop which tests for the flag variable. Notice that whenever the while loop tests for the value of the flag variable it does so while holding the mutex! Within the while loop, we first release the mutex so that the other thread can get a chance to acquire it and do a similar test and then immediately reacquire the mutex before looping over to test the while condition. The pong() method is similar and the complete code appears in the code widget below.

Press + to interact
using System;
using System.Threading;
class Demonstration
{
static void Main()
{
new MutexPingPongExample().runTest();
}
}
public class MutexPingPongExample
{
Mutex mutex = new Mutex();
bool flag = false;
void ping()
{
while (true)
{
mutex.WaitOne();
while (flag)
{
mutex.ReleaseMutex();
mutex.WaitOne();
}
Console.WriteLine("Ping");
flag = true;
mutex.ReleaseMutex();
}
}
void pong()
{
while (true)
{
mutex.WaitOne();
while (!flag)
{
mutex.ReleaseMutex();
mutex.WaitOne();
}
Console.WriteLine("Pong");
flag = false;
mutex.ReleaseMutex();
}
}
public void runTest()
{
Thread pingThread = new Thread(() =>
{
ping();
});
Thread pongThread = new Thread(() =>
{
pong();
});
pingThread.IsBackground = true;
pongThread.IsBackground = true;
pingThread.Start();
pongThread.Start();
Thread.Sleep(10000);
}
}

Note that we mark the two ping and pong threads as background so that the code widget doesn't time out. When the main thread exits the two spawned threads also terminate.

Named Mutex

We can also create named system mutexes, that span multiple applications and are visible throughout the operating system. They can be used for coordinating across multiple processes. Unnamed mutexes are also known as local mutexes and restricted to a single process.

Named Mutex

We can also create named system mutexes, that span multiple applications and are visible throughout the operating system. They can be used for coordinating across multiple processes. Unnamed mutexes are also known as local mutexes and restricted to a single process.

Usage Pattern

The most common newbie mistake is to use mutex in a way such that the possibility of not releasing the mutex after acquiring it exists. For instance, consider the following snippet:

Press + to interact
// Class variable mutex
Mutex mutex = new Mutex();
// snippet within some method
mutex.WaitOne();
try {
// ...
// ... Critical Section
// ...
mutex.ReleaseMutex();
}
catch (Exception e) {
// Handle exception
}

If there's an exception in the critical section, the acquired mutex will not be released and if the thread that locked it, is still alive, no other thread will be able to acquire the mutex, potentially causing a deadlock.

Remember to always unlock the mutex in a finally block when appropriate.

Press + to interact
// Class variable mutex
Mutex mutex = new Mutex();
// snippet within some method
mutex.WaitOne();
try {
// ...
// ... Critical Section
// ...
}
catch (Exception e) {
// Handle exception
}
finally {
// Unlock in a finally block
mutex.ReleaseMutex();
}
Press + to interact
using System;
using System.Threading;
class Demonstration
{
static void Main()
{
// First argument is name of the mutex
// and second specifies if the thread
// creating the mutex should also own it
Mutex mutex = new Mutex("", false);
try {
mutex.WaitOne();
// Critical section
}
catch(Exception e) {
// Handle exceptions here
}
finally {
// REMEMBER TO RELEASE THE MUTEEX IN THE
// FINALLY BLOCK
mutex.ReleaseMutex();
}
}
}

Question # 1

Consider the snippet below:

Press + to interact
Mutex mutex = new Mutex();
mutex.WaitOne();
mutex.WaitOne();
Console.WriteLine("Program Exiting");
mutex.ReleaseMutex();
mutex.ReleaseMutex();
Q

What is the outcome of the above program?

A)

Program hangs because mutex isn’t reentrant

B)

Program throws an exception because mutex is being locked twice in succession

C)

Program exits normally because mutex is reentrant

Press + to interact
using System;
using System.Threading;
class Demonstration
{
static void Main()
{
Mutex mutex = new Mutex();
mutex.WaitOne();
mutex.WaitOne();
Console.WriteLine("Program Exiting");
mutex.ReleaseMutex();
mutex.ReleaseMutex();
}
}

Question # 2

Consider the snippet below:

Press + to interact
Mutex mutex = new Mutex();
mutex.WaitOne();
Console.WriteLine("Program Exiting");
Q

What is the outcome of the above program?

A)

Program hangs because mutex isn’ released when the thread exits

B)

Program throws an exception because mutex isn’t released before exiting

C)

Program exits normally

Press + to interact
using System;
using System.Threading;
class Demonstration
{
static void Main()
{
Mutex mutex = new Mutex();
mutex.WaitOne();
Console.WriteLine("Program Exiting");
}
}

Question # 3

Consider the snippet below:

Press + to interact
Mutex mutex = new Mutex();
Thread t1 = new Thread(() => {
mutex.WaitOne();
// Exit without releasing mutex
Console.WriteLine("t1 exiting");
});
t1.Start();
t1.Join();
Thread t2 = new Thread(() => {
// Acquire an unreleased mutex
mutex.WaitOne();
Console.WriteLine("t2 exiting");
});
t2.Start();
t2.Join();
Q

What is the outcome of running the above snippet?

A)

Program hangs because t1 exits without releasing the mutex

B)

Program exits normally

C)

AbandonedMutexException is thrown when t2 attempts to acquire the mutex

Press + to interact
using System;
using System.Threading;
class Demonstration
{
static void Main()
{
Console.WriteLine(Environment.OSVersion);
Console.WriteLine(Environment.Version);
Mutex mutex = new Mutex();
Thread t1 = new Thread(() => {
mutex.WaitOne();
Console.WriteLine("t1 exiting");
});
t1.Start();
t1.Join();
Thread t2 = new Thread(() => {
mutex.WaitOne();
Console.WriteLine("t2 exiting");
});
t2.Start();
t2.Join();
}
}

The above snippet when run completes successfully, however, running the same snippet on a Mac or Windows will result in an exception. The official documentation states the same. However, the code widget runs Unix and the differences might be a bug/portability issue.

Note that the AbandonedMutexException is only thrown when another thread attempts to wait on a mutex that has not been released by a thread that has exited.

Question # 4

Consider the snippet below:

Press + to interact
Mutex mutex = new Mutex();
Thread t1 = new Thread(() => {
try {
mutex.WaitOne();
DoImportantWork();
mutex.ReleaseMutex();
} catch(Exception) {
// swallow exception
Console.WriteLine("Swallowing exception");
}
});
t1.Start();
t1.Join();
Q

As tech-lead you come across this snippet for code-review, what feedback seems appropriate?

A)

Looks good check it in.

B)

Move the mutex.ReleaseMutex() call to a finally block. In case, the method DoImportantWork() throws, we neatly unlock the mutex.

C)

Declare mutex in a using statement which will automatically unlock the mutex when the scope ends

Press + to interact
using System;
using System.Threading;
class Demonstration
{
static void DoImportantWork() {
throw new SystemException();
}
static void Main()
{
Mutex mutex = new Mutex();
Thread t1 = new Thread(() => {
try {
mutex.WaitOne();
DoImportantWork();
mutex.ReleaseMutex();
} catch(Exception) {
// swallow exception
Console.WriteLine("Swallowing exception");
}
});
t1.Start();
t1.Join();
}
}

Question # 5

Consider the snippet from the previous question and the developer now uses a lock statement as follows:

Press + to interact
Mutex mutex = new Mutex();
Thread t1 = new Thread(() =>
{
try
{
lock (mutex)
{
DoImportantWork();
}
}
catch (Exception)
{
// swallow exception
Console.WriteLine("exception swallowed");
}
});
t1.Start();
t1.Join();
Q

What is appropriate feedback to give in the code review?

A)

Looks good, check it in.

B)

mutex needs to be explcitly released in a finally block

C)

Release the mutex in a finally block

Question # 6

Consider the following snippet:

Press + to interact
using(var mutex = new Mutex())
{
mutex.WaitOne()
DoImportantWork();
mutex.ReleaseMutex();
}
Q

When code-reviewing the above snippet do you spot any bugs?

A)

Nope. All looks good.

B)

When the using scope ends, it’ll call Dispose() on the mutex, however, if DoImportantWork() throws an exception which is swallowed, the mutex would remain locked as Dispose() doesn’t release the mutex.

C)

Snippet should be wrapped in try-catch block.

Question # 7

Consider the snippet below:

Press + to interact
Mutex mutex = new Mutex();
Thread t1 = new Thread(() =>
{
mutex.WaitOne();
mutex.WaitOne();
mutex.ReleaseMutex();
mutex.ReleaseMutex();
});
t1.Start();
t1.Join();
// Main thread attemps to acquire the mutex
mutex.WaitOne();
Console.WriteLine("All Good");
mutex.ReleaseMutex();
Q

What will be the output of running the above program?

A)

“All Good” is printed on the console.

B)

Program execution hangs.

C)

AbandonedMutexException is thrown because the mutex isn’t released as many times as it is acquired.

Press + to interact
using System;
using System.Threading;
class Demonsttration
{
static void Main()
{
Mutex mutex = new Mutex();
Thread t1 = new Thread(() =>
{
// Child thread locks the mutex
// twice but releases it only once
mutex.WaitOne();
mutex.WaitOne();
mutex.ReleaseMutex();
});
t1.Start();
t1.Join();
// Main thread attemps to acquire the mutex
mutex.WaitOne();
Console.WriteLine("All Good");
mutex.ReleaseMutex();
}
}