In this article, we are going to learn how to use the Mutex class in C#.

Multithreading can significantly boost an application’s performance, but it also brings challenges related to synchronizing and coordinating tasks across different threads (and even processes). With C#, we have a variety of tools at our disposal to overcome these challenges and build a resilient, performant application that makes full use of the available computing resources. One such tool is the Mutex class.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

What Are Synchronization Primitives?

In many cases, trying to access a resource simultaneously from multiple threads will cause corruption. For example, we can only write to a file from one place at a time—otherwise, the operations will mangle the data up and the file will get corrupt! Synchronization primitives are tools we have at our disposal to safely access these types of resources from a multi-threaded environment. They do this by making sure we don’t make more simultaneous requests to a resource than is acceptable.

Overview of a Mutex in C#

A Mutex is one of the synchronization primitives that the operating system provides which we can use in C#. Simply put, a Mutex restricts access to a resource so that only one thread can access it at a time.

It does this through the concept of ownership: threads must only access the resource protected by the Mutex if they own the Mutex. Any thread may attempt to acquire ownership of the Mutex upon which one of these outcomes may happen:

  • If the Mutex is free, the requesting thread will successfully acquire its ownership
  • If another thread currently owns the Mutex, the requesting thread will be blocked until the currently owning thread releases it
  • If the thread that currently owns the Mutex (or last owned it) exits without releasing it, an AbandonedMutexException exception will be thrown.

This means that it is not possible for more than one thread to own the Mutex at once.

Basic Usage of a Mutex in C#

Let’s create a Mutex using its constructor:

var mutex = new Mutex();

Doing this, we’ll get a Mutex that’s local and that no thread initially owns. If we instead want the current thread to own the Mutex, we can specify the first parameter, initiallyOwned as true. We’ll soon learn what we mean by a local Mutex and also create a Mutex that isn’t local.

We should have a reference to the Mutex object from each thread we might need to access a protected resource in. Once that need arises, from that same thread we want to access the resource, we’ll invoke its WaitOne method:

mutex.WaitOne();

This method will only return once we’ve acquired ownership of the Mutex. After this point, we can safely access the protected resource without the risk of a race condition.

Once we’re done with the resource, we must take care to release the Mutex so that other threads can acquire it:

mutex.ReleaseMutex();

If our thread terminates without releasing the Mutex, it will be put in an abandoned state. The next thread that acquires the Mutex will encounter an AbandonedMutexException exception on the WaitOne method. It’s good practice to handle this exception when waiting on a Mutex.

Named System Mutexes in C#

Local Mutex objects are sufficient for coordinating access to a protected resource in a single process. However, if we have multiple processes that need to access a single resource, we need a Mutex that can span all those processes. We call this type of Mutex a named system Mutex—it has a name associated with it and any process on the system can access the same Mutex with its name.

To create a named system Mutex, we provide the name parameter to the constructor (this is the second parameter):

var mutex = new Mutex(initiallyOwned: false, "MyMutex");

All Mutex objects on the system with this name refer to the same underlying Mutex in the operating system. The rules we discussed still apply: only one thread can own this Mutex at a time (even across different Mutex instances as long as they all have the same name).

We can prefix Mutex names with either Global\ or Local\. The exact behavior of these prefixes varies by platform, but Mutex instances with the former prefix are generally visible throughout the entire system; those prefixed with the latter may be more isolated.

On certain platforms which isolate processes well, we may have to prefix all Mutex names with Global\ to get the behavior we expect.

Example Mutex Use-Case: File IO

Let’s start with a very simple method that writes some data to a file:

public static void WriteNumbers(string fileName)
{
    for (int number = 1; number <= 50; number++)
    {
        File.AppendAllText(fileName, $"{number} ");
        Thread.Sleep(100);
    }
}

It appends all integers from 1 to 50 to the file each time we execute it.

However, we haven’t implemented any synchronization between threads or processes yet. If we execute this code multiple times simultaneously, we will see multiple different number streams interweaved into the file.

Let’s fix this:

public static void WriteNumbers(string fileName)
{
    using var mutex = new Mutex(initiallyOwned: false, "Global\\numbers_output");

    mutex.WaitOne();

    for (var number = 1; number <= 50; number++)
    {
        File.AppendAllText(fileName, $"{number} ");
        Thread.Sleep(100);
    }

    mutex.ReleaseMutex();
}

We create a named system Mutex to synchronize access to the file. We then acquire ownership of the Mutex before beginning to write the numbers. Finally, after writing to the file, we release the Mutex.

Now, even if we run this code multiple times simultaneously, we won’t get any interweaving between the different number streams. That’s because we’re only writing to the file while we have ownership of the Mutex, and that can only be the case with one thread at a time.

Error Handling in Mutex-Protected Code

In our code, we haven’t implemented any error handling. The ReleaseMutex method will never be called if we encounter an exception in our loop. The next thread to acquire this Mutex will face an AbandonedMutexException exception.

Another scenario we haven’t considered is if the Mutex is in an abandoned state when we try to acquire it.

Let’s solve that:

public static void WriteNumbers(string fileName)
{
    using var mutex = new Mutex(initiallyOwned: false, "Global\\numbers_output");

    try
    {
        mutex.WaitOne();
    }
    catch (AbandonedMutexException)
    {
        File.WriteAllText(fileName, string.Empty);
    }

    try
    {
        for (var number = 1; number <= 50; number++)
        {
            File.AppendAllText(fileName, $"{number} ");
            Thread.Sleep(100);
        }
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

Now, if the last owner of the Mutex abandoned it, we assume that the file is corrupt and clear its contents. We’ll then proceed with writing the numbers as usual.

We also guarantee that we call the ReleaseMutex method if the loop throws an exception.

Conclusion

With the Mutex class in C#, we can easily overcome a lot of the different challenges we face when trying to access a shared resource from a multi-threaded environment. We can offload the heavy lifting to the operating system and rely on its guarantees to write safe, performant code that makes the maximum use of all the resources it has available.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!