Mutex
Intro
Mutex is an OS-backed synchronization primitive that enforces single-owner access to a critical section. In .NET it is most useful for cross-process coordination via named mutexes — for example, ensuring only one instance of a Windows service writes to a shared log file, or preventing concurrent database migrations from two deployment slots. For purely in-process code, lock or SemaphoreSlim is a better default because they avoid the kernel transition overhead that makes Mutex 10-50x slower than Monitor.Enter for uncontended acquisitions.
How It Works
Mutex has ownership semantics:
- A thread acquires ownership with
WaitOne. - Other waiters block until the owner releases it.
- The owning thread must call
ReleaseMutex. - Named mutexes can coordinate multiple processes on the same machine;
Global\/Local\prefixes are Windows Terminal Services scope controls, not general cross-platform naming defaults.
Example
using var mutex = new Mutex(initiallyOwned: false, name: "MyApp.SingleWriter");
if (!mutex.WaitOne(TimeSpan.FromSeconds(1)))
{
return;
}
try
{
WriteSharedFile();
}
finally
{
mutex.ReleaseMutex();
}
Single-instance application guard using a global mutex:
// Global\ prefix makes the mutex visible across all Terminal Services sessions on Windows
const string MutexName = @"Global\MyApp.SingleInstance";
using var mutex = new Mutex(initiallyOwned: false, name: MutexName, createdNew: out bool created);
if (!mutex.WaitOne(0)) // non-blocking check
{
Console.Error.WriteLine("Another instance is already running.");
return;
}
try
{
RunApplication();
}
finally
{
mutex.ReleaseMutex();
}
Pitfalls
- Kernel transition overhead on hot paths —
Mutex.WaitOneis a kernel call that costs 1-5 µs per uncontended acquisition, versus 20-50 ns forlock(which usesMonitor.Enterwith a user-mode spin before escalating). An API endpoint usingMutexfor in-process synchronization at 10K req/s adds 10-50 ms of cumulative wait time per second. Uselockfor in-process,Mutexonly when cross-process is required. - Release from wrong thread throws — calling
ReleaseMutexfrom a thread that does not own it throwsApplicationException. In async code where continuations can run on different threads, this is a landmine. KeepWaitOne/ReleaseMutexin the same synchronous method scope; for async patterns, useSemaphoreSlim.WaitAsyncinstead. - Abandoned mutex corruption risk — if the owner thread exits without releasing (crash,
Thread.Abort, unhandled exception), the next waiter getsAbandonedMutexException. This means the protected resource may be in an inconsistent state. Always validate shared state after acquiring an abandoned mutex. - Security on shared machines — named mutexes are not automatically restricted to your process or user context. On Windows, another process can open your named mutex and interfere with coordination. Use
MutexAccessRule/MutexSecurityto restrict access. On Linux, named mutexes use shared memory files with no ACL support — avoid named mutexes on multi-tenant machines.
Tradeoffs
Mutexvslock: mutex supports cross-process coordination, whilelockis faster and simpler for in-process synchronization.MutexvsSemaphore: mutex serializes to one owner; semaphore allows bounded parallel entrants.MutexvsSemaphoreSlim:SemaphoreSlimis better for in-process async throttling, but it cannot be named for cross-process locking.
Questions
Mutex the right tool in .NET?When you need to coordinate access across multiple processes on the same machine (for example single-writer protection for shared file/database artifacts).
Mutex often a poor default for web request hot paths?
It is OS-backed and blocking, so heavy contention can increase latency. In-process patterns (lock, SemaphoreSlim, Channel<T>) are usually more efficient.
AbandonedMutexException signal?A previous owner exited without releasing the mutex, which means exclusive ownership was recovered but shared state may be inconsistent and must be validated.
Links
- Mutex class (Microsoft Learn)
- Overview of synchronization primitives (Microsoft Learn)
- Managed threading best practices (Microsoft Learn)
- Threading in C#: Event wait handles, mutexes, and semaphores (Joe Albahari)