The Misunderstood Mutex

Friday, August 20, 2004

Someone should delete this article in purgatory at codeproject.com. The article is full of mis-information, but look at the view count. 

The article attempts to restrict a Windows application to a single running instance. The article tries to do this using the Process class from the System.Diagnostics namespace. The code invokes Process.GetProcessesByName(processName) to see if there are any existing processes running with the same name, and exits if another is found. If you do some searching, you’ll find other code snippets using the same technique.

There are at least three problems with this approach:

1) It doesn’t account for race conditions. Two instances of the application could launch at nearly the same time, see each other, and both shut down.

2) It doesn’t work in terminal services, at least not if I want the application to run an instance in each login session.

3) It doesn’t account for the possibility that someone else might have a process with the same name.

These problems might be considered ‘edge conditions’, except there is an easy, foolproof way to check for a running instance of an application. A named mutex allows multiple processes to use the same mutex object for interprocess synchronization. The author asserts a mutex is not safe on a multiprocessor machine. If this were true it would be the end of civilization as we know it.

The name of the mutex will preferably be a unique identifier to offset the chances of another application using the same name. One could chose to use the full name of the executing assembly, or a GUID. If the application can acquire the named mutex with WaitOne, then it is the first instance running. If the application calls WaitOne with a timeout value and WaitOne returns false, another instance of the application is running and this one needs to exit.

When using this approach in .NET there is one ‘gotcha’. The following code has a small problem:

[STAThread]
static void Main() 
{
   Mutex mutex = new Mutex(false, appGuid);
   if(!mutex.WaitOne(0, false))
   {
      MessageBox.Show("Instance already running");
      return;
   }
   Application.Run(new Form1());
}
private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";

The problem is easy to reproduce if you run the following code in a release build:

[STAThread]
static void Main() 
{
   Mutex mutex = new Mutex(false, appGuid);
   if(!mutex.WaitOne(0, false))
   {
      MessageBox.Show("Instance already running");
      return;
   }
   GC.Collect();                
   Application.Run(new Form1());
}
private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";

Since the mutex goes unused when the Form starts running, the compiler and garbage collector are free to conspire together to collect the mutex out of existence. After the first garbage collector run, one might be able to launch multiple instances of the application again. The following code will keep the mutex alive. (The call to GC.Collect is still here just for testing).

[STAThread]
static void Main() 
{
   Mutex mutex = new Mutex(false, appGuid);
   if(!mutex.WaitOne(0, false))
   {
      MessageBox.Show("Instance already running");
      return;
   }
         
   GC.Collect();                
   Application.Run(new Form1());
   GC.KeepAlive(mutex);
}
private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";

There is still an imperfection in the code. Mutex derives from WaitHandle, and WaitHandle implements IDisposable. Here is one more example that keeps the mutex alive and properly disposes the mutex when finished.

[STAThread]
static void Main() 
{
   using(Mutex mutex = new Mutex(false, appGuid))
   {
      if(!mutex.WaitOne(0, false))
      {
         MessageBox.Show("Instance already running");
         return;
      }
   
      GC.Collect();                
      Application.Run(new Form1());
   }
}

With the above code I can run the application from the console, and also log into the machine with terminal services and run the application in a different session. Terminal services provides a unique namespace for each client session (so does fast user switching on Windows XP). When I create a named mutex, the mutex lives inside the namespace for the session I am running in. Like .NET namespaces, terminal services uses namespaces to prevent naming collisions.

If I want to have only one instance of the application running across all sessions on the machine, I can put the named mutex into the global namespace with the prefix “Global\”.

[STAThread]
static void Main() 
{
   using(Mutex mutex = new Mutex(false, @"Global\" + appGuid))
   {
      if(!mutex.WaitOne(0, false))
      {
         MessageBox.Show("Instance already running");
         return;
      }
   
      GC.Collect();                
      Application.Run(new Form1());
   }
}

After all this you may find you need to adjust permissions on the mutex in order to access the mutex from another process running with different credentials than the first. This requires some PInvoke work and deserves a post unto itself.


Comments
Thomas Eyde Friday, August 20, 2004
Very well explained. The "using" approach is the best. Very OO, very C#. Let it be the template on how to do this.
<br>
Ernst Kuschke Tuesday, August 24, 2004
Cool post - I like the using clause around the mutex's scope better than my example [1].
<br>
<br>[1] - http://dotnet.org.za/ernst/archive/2004/07/20/2887.aspx
Andrew Wiseman Thursday, December 2, 2004
Interesting article. I liked the &quot;using&quot; too - but can't fathom how to convert it to VB.NET.
<br>
<br>I found an article by Microsoft entitled &quot;How to write an application that supports the Fast User Switching feature by using Visual Basic .NET in Windows XP&quot; which also addresses the multiple instances problem. It explains how you can switch to the running application if you attempt to run another instance. But sadly it also suggests using GetProcessesByName to do the detection.
Phil Rowe Thursday, June 9, 2005
A very good solution which will work for both local machines and via Terminal Services. The Terminal Services aspect of this was proving a real show-stopper (using GetProcessesByName), but now I have a life again!
Sean Terry Tuesday, June 21, 2005
Very nice! I've hated the GetProcessByName method, since it seemed like so much duct-tape.
<br>
<br>I wanted to create a single-instance of my application, but also be able to pass messages from any new instances before terminating them. Remoting solves the message-passing problem, but I needed some way to prevent a race condition if a second instance was created before the server object was set up in the first.
<br>
<br>This solves all my problems :)
Al Gonzalez Monday, July 11, 2005
While your description of the problem seems reasonable, I can't duplicate it.

If I run the second snippet of code, I still am only able to get one instance of the application.

I even tried adding GC.WaitForPendingFinalizers();
and had the same results.

This was in C# and VB.NET using the .NET Framework v1.1.
John Klucker Sunday, March 26, 2006
I tried your code using c# 2.0 and found the even if I use the using keyword I still do need the GC.KeepAlive(mutex) in order for it to work right.

string mutexName = "Local\\" +
System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

using (Mutex mutex = new Mutex(false, mutexName))
{
if(!mutex.WaitOne(0, false))
{
MessageBox.Show("Instance of application already running!", "Law Billing");
return;
}

GC.Collect();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
GC.KeepAlive(mutex);
}
Steve Monday, March 27, 2006
Wouldn't it be better the release the mutex at the end of your program, as opposed to just keeping it alive? I'm new to c#, but explicitly releasing the mutex seems better practice, and referencing it to get the release method should force the garbage collector to leave it alone.
scott Tuesday, March 28, 2006
John: That's odd. Is this on .NET 1.1 or 2.0?

Steve: Good point.
Dan Frangiamore Saturday, October 14, 2006
To John K. (regarding still needing KeepAlive()), if I understand the article correctly, the author may have accidentally coded the line GC.Collect() when he meant to type GC.KeepAlive(mutex).

The reason for my claim is that the purpose of the Using statement is to forcibly dispose of an object being used when code execution reaches the end of the block. The author is probably saying that the end of the "Using" block is the perfect time to keep it alive (GC.KeepAlive(mutex)) because it will be promptly disposed at End Using.

Regarding a Visual Basic Implementation of this article's technique, I have found that my version of the .NET framework does not support "Using", for some reason. So I have come up with this:

' ---------------------------
' Instance Control
' ---------------------------

' create an id so the machine can recognize this app
Dim mutexID As String = "eb6dc9ad-cdd1-426e-8417-a37a7fc40558-My-App.."

' create mutex
Dim mutex As New System.Threading.Mutex(False, mutexID)

' test for another instance
If Not mutex.WaitOne(0, False) Then

' notify user, then quit
MsgBox("An instance is already running and this one will close.")

' quit
End

End If

' keep the mutex alive before it goes out of scope..
GC.KeepAlive(mutex)
Agnel CJ Kurian Thursday, December 21, 2006
Scott, here's a little program I am using to test Mutexes:

using System.Threading;
using System.Diagnostics;

class Mutt
{
static void Work()
{
System.Console.WriteLine("PID: {0}", Process.GetCurrentProcess().Id);
for(int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
System.Console.Write(".");
}

System.Console.Write("\n");
}

public static void Main(string[] args)
{
bool live;
Mutex m = new Mutex(true, "symon", out live);

if(live)
{
Work();
m.ReleaseMutex();
}

while(true)
{
m.WaitOne();
Work();
m.ReleaseMutex();
}
}
}

I believe that if I run two instances of this program, then they would call the Work() method alternately. i.e each instance gets a shot at Work() and then waits till another instance also calls Work(). So...if only one instance were running then it would call Work() only once and wait infinitely. Am I right? But...the program does not work as expected.
Mutex Mutt Wednesday, January 24, 2007
The code posted here doesn't work. I did this instead and was able to get it to work properly.

using System.Reflection;
using System.IO;
using System.Threading;

static Mutex mutex;

string strLoc = Assembly.GetExecutingAssembly().Location;

FileSystemInfo fileInfo = new FileInfo(strLoc);

string sExeName = fileInfo.Name;
bool bCreatedNew;

mutex = new Mutex(true, "Global\\"+sExeName, out bCreatedNew);
if (bCreatedNew)
mutex.ReleaseMutex();

if (!bCreatedNew)
{
MessageBox.Show("App Running!", "App Already Running", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!