405

The Mutex class is very misunderstood, and Global mutexes even more so.

What is good, safe pattern to use when creating Global mutexes?

One that will work

  • Regardless of the locale my machine is in
  • Is guaranteed to release the mutex properly
  • Optionally does not hang forever if the mutex is not acquired
  • Deals with cases where other processes abandon the mutex
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506

7 Answers7

440

I want to make sure this is out there, because it's so hard to get right:

using System.Runtime.InteropServices;   //GuidAttribute
using System.Reflection;                //Assembly
using System.Threading;                 //Mutex
using System.Security.AccessControl;    //MutexAccessRule
using System.Security.Principal;        //SecurityIdentifier

static void Main(string[] args)
{
    // get application GUID as defined in AssemblyInfo.cs
    string appGuid =
        ((GuidAttribute)Assembly.GetExecutingAssembly().
            GetCustomAttributes(typeof(GuidAttribute), false).
                GetValue(0)).Value.ToString();

    // unique id for global mutex - Global prefix means it is global to the machine
    string mutexId = string.Format( "Global\\{{{0}}}", appGuid );

    // Need a place to store a return value in Mutex() constructor call
    bool createdNew;

    // edited by Jeremy Wiebe to add example of setting up security for multi-user usage
    // edited by 'Marc' to work also on localized systems (don't use just "Everyone") 
    var allowEveryoneRule =
        new MutexAccessRule( new SecurityIdentifier( WellKnownSidType.WorldSid
                                                   , null)
                           , MutexRights.FullControl
                           , AccessControlType.Allow
                           );
    var securitySettings = new MutexSecurity();
    securitySettings.AddAccessRule(allowEveryoneRule);

   // edited by MasonGZhwiti to prevent race condition on security settings via VanNguyen
    using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings))
    {
        // edited by acidzombie24
        var hasHandle = false;
        try
        {
            try
            {
                // note, you may want to time out here instead of waiting forever
                // edited by acidzombie24
                // mutex.WaitOne(Timeout.Infinite, false);
                hasHandle = mutex.WaitOne(5000, false);
                if (hasHandle == false)
                    throw new TimeoutException("Timeout waiting for exclusive access");
            }
            catch (AbandonedMutexException)
            {
                // Log the fact that the mutex was abandoned in another process,
                // it will still get acquired
                hasHandle = true;
            }

            // Perform your work here.
        }
        finally
        {
            // edited by acidzombie24, added if statement
            if(hasHandle)
                mutex.ReleaseMutex();
        }
    }
}
Jani
  • 526
  • 4
  • 6
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506
  • 1
    you may want to omit `using` to check `createdNew` and add `mutex.Dispose()` inside `finally`. I can't explain it clearly (I don't know the reason) right now but I've got myself to a situation when `mutex.WaitOne` returned `true` after `createdNew` became `false` (I acquired the mutex in the current `AppDomain` and then loaded a new `AppDomain` and executed the same code from within it). – Sergey.quixoticaxis.Ivanov Feb 07 '17 at 14:44
  • 1. Does `exitContext = false` do anything in `mutex.WaitOne(5000, false)`? [It looks like it could only cause an assert in CoreCLR](https://github.com/dotnet/coreclr/blob/a9b25d4aa22a1f4ad5f323f6c826e318f5a720fe/src/vm/comwaithandle.cpp#L164), 2. If anyone's wondering, in `Mutex`'s constructor, the reason why `initiallyOwned` is `false` is partially explained by [this MSDN article](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx). – jrh Nov 01 '17 at 17:30
  • 5
    A tip: watch out using Mutex with ASP.NET: "The Mutex class enforces thread identity, so a mutex can be released only by the thread that acquired it. By contrast, the Semaphore class does not enforce thread identity.". An ASP.NET request can be serviced by multiple threads. – Sam Rueby Nov 14 '17 at 20:58
  • *startupnextinstance* event safely in VB.NET? not in C# https://learn.microsoft.com/es-es/dotnet/api/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.startupnextinstance?view=netframework-4.8 – Kiquenet Aug 14 '19 at 20:35
  • See my answer without using WaitOne. https://stackoverflow.com/a/59079638/4491768 – Wouter Nov 27 '19 at 23:10
  • Where to put this code into a default WPF application? (.NET 4.X, where Main is in auto generated App.g.i.cs) – oo_dev Dec 08 '21 at 08:22
  • 1
    Is there an equivalent pattern for this on Unix systems? (The MutexAccessRule, MutexRights, MutexSecurity implementations are Windows-only) – Zander Fick Jan 12 '22 at 10:29
  • Only to confirm the pattern: After an ````AbandonedException```` during acquire the mutex is indeed acquired by the thread receiving this exception: https://learn.microsoft.com/en-us/dotnet/api/system.threading.abandonedmutexexception?view=netframework-4.8 – Niklas Peter Mar 04 '22 at 07:42
  • Btw, since `createdNew` is not being used, in C#7 the out parameter can be [discarded](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/discards) by passing underscore instead, i.e. `new Mutex(false, mutexId, out _, securitySettings))`. – vgru Oct 14 '22 at 07:14
144

Using the accepted answer I create a helper class so you could use it in a similar way you would use the Lock statement. Just thought I'd share.

Use:

using (new SingleGlobalInstance(1000)) //1000ms timeout on global lock
{
    //Only 1 of these runs at a time
    RunSomeStuff();
}

And the helper class:

class SingleGlobalInstance : IDisposable
{
    //edit by user "jitbit" - renamed private fields to "_"
    public bool _hasHandle = false;
    Mutex _mutex;

    private void InitMutex()
    {
        string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value;
        string mutexId = string.Format("Global\\{{{0}}}", appGuid);
        _mutex = new Mutex(false, mutexId);

        var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
        var securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);
        _mutex.SetAccessControl(securitySettings);
    }

    public SingleGlobalInstance(int timeOut)
    {
        InitMutex();
        try
        {
            if(timeOut < 0)
                _hasHandle = _mutex.WaitOne(Timeout.Infinite, false);
            else
                _hasHandle = _mutex.WaitOne(timeOut, false);

            if (_hasHandle == false)
                throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
        }
        catch (AbandonedMutexException)
        {
            _hasHandle = true;
        }
    }


    public void Dispose()
    {
        if (_mutex != null)
        {
            if (_hasHandle)
                _mutex.ReleaseMutex();
            _mutex.Close();
        }
    }
}
deepee1
  • 12,878
  • 4
  • 30
  • 43
  • Awesome work, thanks! FYI: I've updated the Dispose method above to prevent the CA2213 warning during Code Analysis. The rest passed fine. For more detail check http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k(CA2213);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.0);k(DevLang-csharp)&rd=true – Pat Hermens Dec 27 '12 at 08:05
  • 1
    Semantically the (new SingleGlobalInstance(xxx)) leads one to believe that each mutex is distinct when in reality they all refer to the same one. Wouldn't it be clearer to create a 'using (new MutexLocker(Mutex mutexOpt = null)", where it creates/defaults to a static mutex that is created exactly once per app (perhaps hidden within the class like you have done)? Also "Global" usually implies "application wide" whereas System is "server wide" which I believe is what a named mutex is. http://msdn.microsoft.com/en-us/library/hw29w7t1.aspx – crokusek Oct 15 '13 at 21:45
  • 3
    How do I handle the timeout exception in the class that consumes SingleGlobalInstance. Also is it good practice to throw exception while constructing an instance ? – kiran Feb 11 '14 at 15:57
  • +1 this is excellent for situations where you need the resilience of the code in the accepted answer, but don't want repeated boilerplate code lying around everywhere cluttering up your business logic. Thankyou! – theyetiman Feb 24 '14 at 17:30
  • 4
    A timeout of 0 should still be a timeout of zero, not infinity! Better check for `< 0` instead of `<= 0`. – ygoe Feb 25 '14 at 16:41
  • Is it possible to get an exception when releasing mutex in Dispose? If so it seems like mutex.Dispose() does not work and mutex remains as not disposed. We shall add a try-finally block and dispose mutex in finally block. – antistar Apr 09 '14 at 14:46
  • 3
    @antistar: I found that using `_mutex.Close()` instead of `_mutex.Dispose()` in the Dispose method worked for me. The error was cause by trying to dispose of the underlying WaitHandle. `Mutex.Close()` disposes of the underlying resources. – djpMusic May 22 '14 at 19:28
  • 1
    It shows "AppName has stopped working." when I try to open second instance of app. I want to set the focus on the app when the user tries to open the second instance of the app. How can I do it? – Bhaskar Jan 29 '15 at 02:39
  • Checking for `_hasHandle` in `Dispose` is superfluous, since if the constructor throws, `Dispose` won't be called, and if the constructor doesn't throw, `_hasHandle` will be true. – wezten Nov 07 '19 at 11:32
10

This example will exit after 5 seconds if another instance is already running.

// unique id for global mutex - Global prefix means it is global to the machine
const string mutex_id = "Global\\{B1E7934A-F688-417f-8FCB-65C3985E9E27}";

static void Main(string[] args)
{

    using (var mutex = new Mutex(false, mutex_id))
    {
        try
        {
            try
            {
                if (!mutex.WaitOne(TimeSpan.FromSeconds(5), false))
                {
                    Console.WriteLine("Another instance of this program is running");
                    Environment.Exit(0);
                }
            }
            catch (AbandonedMutexException)
            {
                // Log the fact the mutex was abandoned in another process, it will still get aquired
            }

            // Perform your work here.
        }
        finally
        {
            mutex.ReleaseMutex();
        }
    }
}
Liam
  • 19,819
  • 24
  • 83
  • 123
10

Neither Mutex nor WinApi CreateMutex() works for me.

An alternate solution:

static class Program
{
    [STAThread]
    static void Main()
    {
        if (SingleApplicationDetector.IsRunning()) {
            return;
        }

        Application.Run(new MainForm());

        SingleApplicationDetector.Close();
    }
}

And the SingleApplicationDetector:

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Threading;

public static class SingleApplicationDetector
{
    public static bool IsRunning()
    {
        string guid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
        var semaphoreName = @"Global\" + guid;
        try {
            __semaphore = Semaphore.OpenExisting(semaphoreName, SemaphoreRights.Synchronize);

            Close();
            return true;
        }
        catch (Exception ex) {
            __semaphore = new Semaphore(0, 1, semaphoreName);
            return false;
        }
    }

    public static void Close()
    {
        if (__semaphore != null) {
            __semaphore.Close();
            __semaphore = null;
        }
    }

    private static Semaphore __semaphore;
}

Reason to use Semaphore instead of Mutex:

The Mutex class enforces thread identity, so a mutex can be released only by the thread that acquired it. By contrast, the Semaphore class does not enforce thread identity.

<< System.Threading.Mutex

Ref: Semaphore.OpenExisting()

StarPilot
  • 2,246
  • 1
  • 16
  • 18
sol
  • 111
  • 1
  • 2
  • 9
    Possible race conditions between `Semaphore.OpenExisting` and `new Semaphore`. – xmedeko Jun 15 '16 at 11:23
  • To address this issue you could simply include the release inside the finally block of a try/catch/finally. This ensures your mutex doesn't get held open forever. – Ro Yo Mi Oct 08 '22 at 21:51
4

Sometimes learning by example helps the most. Run this console application in three different console windows. You'll see that the application you ran first acquires the mutex first, while the other two are waiting their turn. Then press enter in the first application, you'll see that application 2 now continues running by acquiring the mutex, however application 3 is waiting its turn. After you press enter in application 2 you'll see that application 3 continues. This illustrates the concept of a mutex protecting a section of code to be executed only by one thread (in this case a process) like writing to a file as an example.

using System;
using System.Threading;

namespace MutexExample
{
    class Program
    {
        static Mutex m = new Mutex(false, "myMutex");//create a new NAMED mutex, DO NOT OWN IT
        static void Main(string[] args)
        {
            Console.WriteLine("Waiting to acquire Mutex");
            m.WaitOne(); //ask to own the mutex, you'll be queued until it is released
            Console.WriteLine("Mutex acquired.\nPress enter to release Mutex");
            Console.ReadLine();
            m.ReleaseMutex();//release the mutex so other processes can use it
        }
    }
}

enter image description here

user3248578
  • 905
  • 8
  • 18
4

A solution (for WPF) without WaitOne because it can cause an AbandonedMutexException. This solution uses the Mutex constructor that returns the createdNew boolean to check if the mutex is already created. It also uses the GetType().GUID so renaming an executable doesn't allow multiple instances.

Global vs local mutex see note in: https://learn.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8

private Mutex mutex;
private bool mutexCreated;

public App()
{
    string mutexId = $"Global\\{GetType().GUID}";
    mutex = new Mutex(true, mutexId, out mutexCreated);
}

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    if (!mutexCreated)
    {
        MessageBox.Show("Already started!");
        Shutdown();
    }
}

Because Mutex implements IDisposable it is released automatically but for completeness call dispose:

protected override void OnExit(ExitEventArgs e)
{
    base.OnExit(e);
    mutex.Dispose();
}

Move everything into a base class and add the allowEveryoneRule from the accepted answer. Also added ReleaseMutex though it doesn't look like it's really needed because it is released automatically by the OS (what if the application crashes and never calls ReleaseMutex would you need to reboot?).

public class SingleApplication : Application
{
    private Mutex mutex;
    private bool mutexCreated;

    public SingleApplication()
    {
        string mutexId = $"Global\\{GetType().GUID}";

        MutexAccessRule allowEveryoneRule = new MutexAccessRule(
            new SecurityIdentifier(WellKnownSidType.WorldSid, null),
            MutexRights.FullControl, 
            AccessControlType.Allow);
        MutexSecurity securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowEveryoneRule);

        // initiallyOwned: true == false + mutex.WaitOne()
        mutex = new Mutex(initiallyOwned: true, mutexId, out mutexCreated, securitySettings);        
    }

    protected override void OnExit(ExitEventArgs e)
    {
        base.OnExit(e);
        if (mutexCreated)
        {
            try
            {
                mutex.ReleaseMutex();
            }
            catch (ApplicationException ex)
            {
                MessageBox.Show(ex.Message, ex.GetType().FullName, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        mutex.Dispose();
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        if (!mutexCreated)
        {
            MessageBox.Show("Already started!");
            Shutdown();
        }
    }
}
Wouter
  • 2,540
  • 19
  • 31
0

A global Mutex is not only to ensure to have only one instance of an application. I personally prefer using Microsoft.VisualBasic to ensure single instance application like described in What is the correct way to create a single-instance WPF application? (Dale Ragan answer)... I found that's easier to pass arguments received on new application startup to the initial single instance application.

But regarding some previous code in this thread, I would prefer to not create a Mutex each time I want to have a lock on it. It could be fine for a single instance application but in other usage it appears to me has overkill.

That's why I suggest this implementation instead:

Usage:

static MutexGlobal _globalMutex = null;
static MutexGlobal GlobalMutexAccessEMTP
{
    get
    {
        if (_globalMutex == null)
        {
            _globalMutex = new MutexGlobal();
        }
        return _globalMutex;
    }
}

using (GlobalMutexAccessEMTP.GetAwaiter())
{
    ...
}   

Mutex Global Wrapper:

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;

namespace HQ.Util.General.Threading
{
    public class MutexGlobal : IDisposable
    {
        // ************************************************************************
        public string Name { get; private set; }
        internal Mutex Mutex { get; private set; }
        public int DefaultTimeOut { get; set; }
        public Func<int, bool> FuncTimeOutRetry { get; set; }

        // ************************************************************************
        public static MutexGlobal GetApplicationMutex(int defaultTimeOut = Timeout.Infinite)
        {
            return new MutexGlobal(defaultTimeOut, ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value);
        }

        // ************************************************************************
        public MutexGlobal(int defaultTimeOut = Timeout.Infinite, string specificName = null)
        {
            try
            {
                if (string.IsNullOrEmpty(specificName))
                {
                    Name = Guid.NewGuid().ToString();
                }
                else
                {
                    Name = specificName;
                }

                Name = string.Format("Global\\{{{0}}}", Name);

                DefaultTimeOut = defaultTimeOut;

                FuncTimeOutRetry = DefaultFuncTimeOutRetry;

                var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
                var securitySettings = new MutexSecurity();
                securitySettings.AddAccessRule(allowEveryoneRule);

                Mutex = new Mutex(false, Name, out bool createdNew, securitySettings);

                if (Mutex == null)
                {
                    throw new Exception($"Unable to create mutex: {Name}");
                }
            }
            catch (Exception ex)
            {
                Log.Log.Instance.AddEntry(Log.LogType.LogException, $"Unable to create Mutex: {Name}", ex);
                throw;
            }
        }

        // ************************************************************************
        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public MutexGlobalAwaiter GetAwaiter(int timeOut)
        {
            return new MutexGlobalAwaiter(this, timeOut);
        }

        // ************************************************************************
        /// <summary>
        /// 
        /// </summary>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        public MutexGlobalAwaiter GetAwaiter()
        {
            return new MutexGlobalAwaiter(this, DefaultTimeOut);
        }

        // ************************************************************************
        /// <summary>
        /// This method could either throw any user specific exception or return 
        /// true to retry. Otherwise, retruning false will let the thread continue
        /// and you should verify the state of MutexGlobalAwaiter.HasTimedOut to 
        /// take proper action depending on timeout or not. 
        /// </summary>
        /// <param name="timeOutUsed"></param>
        /// <returns></returns>
        private bool DefaultFuncTimeOutRetry(int timeOutUsed)
        {
            // throw new TimeoutException($"Mutex {Name} timed out {timeOutUsed}.");

            Log.Log.Instance.AddEntry(Log.LogType.LogWarning, $"Mutex {Name} timeout: {timeOutUsed}.");
            return true; // retry
        }

        // ************************************************************************
        public void Dispose()
        {
            if (Mutex != null)
            {
                Mutex.ReleaseMutex();
                Mutex.Close();
            }
        }

        // ************************************************************************

    }
}

Awaiter

using System;

namespace HQ.Util.General.Threading
{
    public class MutexGlobalAwaiter : IDisposable
    {
        MutexGlobal _mutexGlobal = null;

        public bool HasTimedOut { get; set; } = false;

        internal MutexGlobalAwaiter(MutexGlobal mutexEx, int timeOut)
        {
            _mutexGlobal = mutexEx;

            do
            {
                HasTimedOut = !_mutexGlobal.Mutex.WaitOne(timeOut, false);
                if (! HasTimedOut) // Signal received
                {
                    return;
                }
            } while (_mutexGlobal.FuncTimeOutRetry(timeOut));
        }

        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    _mutexGlobal.Mutex.ReleaseMutex();
                }

                // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
                // TODO: set large fields to null.

                disposedValue = true;
            }
        }
        // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
        // ~MutexExAwaiter()
        // {
        //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        //   Dispose(false);
        // }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
            // TODO: uncomment the following line if the finalizer is overridden above.
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}
Eric Ouellet
  • 10,996
  • 11
  • 84
  • 119