Mutex Object, Explained

Suppose you have two threads running simultaneously and they are both using a single instance of a particular object (say a global variable, for example) .  How do you guarantee that only one thread uses that object at any given time?

A mutual exclusion ("mutex") is a mechanism that acts as a flag to prevent two threads from performing one or more actions simultaneously.  The entire action that you want to run exclusively is called a critical section or protected section.

Modern programming languages support this natively.  In C#, it's as simple as:

  1. Instantiating a new static Mutex object that's accessible from each thread
  2. Wrapping whatever code you want to be executed in the critical section with that object's WaitOne() and ReleaseMutex() methods in each thread
An overloaded WaitOne() method also accepts a TimeSpan object, useful if the thread should wait only until a period of time has lapsed.  Typically this is employed when there's a risk of deadlock, where two or more threads are waiting for the same mutex to become available simultaneously.  Deadlock is as bad as it sounds, since it can result in an application that has "stalled out" for all intents and purposes.

Here's a sample .NET 4.0 console application that demonstrates the getting and setting of a static global variable between two threads, first without using the Mutex object and then with it.  In the console output, one thread looped 50 times and one ran 200 times (at faster rate).  Each time it retrieved a value from the global variable that it wasn't expecting, the number of "clashes" was incremented.

Obviously this exercise was written to amplify this disadvantage, but you'll note that there is a large amount of unpredictability (clashes) when not using a Mutex object.  However when wrapping the getting and setting the global variable around a critical section, you'll note that no clashes are observed.  In theory we are introducing a small delay when one thread waits until the other leaves its critical section, but provided that you keep your critical section no larger than is absolutely necessary, slightly slower processing is a small price to pay for the benefit of having a predicable result.

Program.cs:

// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

using System;
using System.Threading;

namespace MutexExample
{
    class Program
    {
        private static string _globalVariable = string.Empty;
        private static readonly Mutex GlobalVariableMutex = new Mutex();

        static void Main(string[] args)
        {
            Console.WriteLine("Press Enter to start");
            Console.ReadLine();
            Console.WriteLine("NOT using mutex:");

            var parametersA = new ThreadParameters { Name = "A", SleepTime = 5, UseMutex = false, LoopCount = 50 };
            var parametersB = new ThreadParameters { Name = "B", SleepTime = 3, UseMutex = false, LoopCount = 200 };

            var threadA = new Thread(GetSetGlobalVariable);
            var threadB = new Thread(GetSetGlobalVariable);

            threadA.Start(parametersA);
            threadB.Start(parametersB);

            // Wait until both threads finish:
            threadA.Join();
            threadB.Join();

            Console.WriteLine(Environment.NewLine + "Using mutex:");

            var parametersC = new ThreadParameters { Name = "C", SleepTime = 5, UseMutex = true, LoopCount = 50 };
            var parametersD = new ThreadParameters { Name = "D", SleepTime = 3, UseMutex = true, LoopCount = 200 };

            var threadC = new Thread(GetSetGlobalVariable);
            var threadD = new Thread(GetSetGlobalVariable);

            threadC.Start(parametersC);
            threadD.Start(parametersD);

            // Wait until both threads finish:
            threadC.Join();
            threadD.Join();

            Console.WriteLine(Environment.NewLine + "Press Enter to exit");
            Console.ReadLine();
        }

        private static void GetSetGlobalVariable(Object obj)
        {
            var threadParams = (ThreadParameters) obj;
            var numClashes = 0;  // Count the number of times the global variable wasn't the value it was expected

            for (var i = 0; i < threadParams.LoopCount; i++)
            {
                // Create a new value for the global variable, using a Guid object to ensure a unique value:
                var expectedValue = string.Format("Thread {0} - {1}", threadParams.Name, Guid.NewGuid());

                // If using a Mutex:
                if (threadParams.UseMutex)
                {
                    // Start the critical area / protected section:
                    GlobalVariableMutex.WaitOne();
                }

                // Set the global variable:
                _globalVariable = expectedValue;

                // Insert an artificial wait, simulating additional processing:
                Thread.Sleep(threadParams.SleepTime);

                // Get the global variable:
                var actualValue = _globalVariable;

                // If using a Mutex:
                if (threadParams.UseMutex)
                {
                    // End the critical area / protected section:
                    GlobalVariableMutex.ReleaseMutex();
                }

                // Compare the global variable with the expected value:
                var isClash = expectedValue != actualValue;
                if (isClash)
                {
                    numClashes++;
                }

                //Console.WriteLine(string.Format("Expecting: {0}{3}Actual: {1}{3}Is a clash: {2}{3}", expectedValue, actualValue, isClash, Environment.NewLine));
            }
            Console.WriteLine(String.Format("Number of Thread {0} clashes: {1} out of {2}", threadParams.Name, numClashes, threadParams.LoopCount));
        }
    }
}

Here's a ThreadParameters class that's used for storing some parameters passed to each of the threads.  It's pretty application-specific.

ThreadParameters.cs:

// Code from www.jaypm.com
// Feel free to use, but please give credit where it's due.

namespace MutexExample
{
    class ThreadParameters
    {
        public string Name { get; set; }
        public bool UseMutex { get; set; }
        public int SleepTime { get; set; }
        public int LoopCount { get; set; }
    }
}

References:
http://en.wikipedia.org/wiki/Mutex
http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx

Leave a Reply

Your email address will not be published. Required fields are marked *