C#
Mutex
Semaphore
Multi-threading
Concurrency

What is the Mutex and semaphore In c? where we need to implement?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In C#, a mutex and a semaphore are synchronization primitives used to coordinate concurrent threads. The simplest difference is capacity: a mutex allows one owner at a time, while a semaphore allows a limited number of concurrent entrants.

What a Mutex Is

A mutex, short for mutual exclusion, protects a critical section so only one thread or process can enter it at once. In .NET, Mutex is heavier than the lock keyword, but it can be named and used across process boundaries.

csharp
1using System;
2using System.Threading;
3
4class Program
5{
6    private static readonly Mutex Mutex = new Mutex();
7    private static int _counter = 0;
8
9    static void Main()
10    {
11        var t1 = new Thread(Increment);
12        var t2 = new Thread(Increment);
13        t1.Start();
14        t2.Start();
15        t1.Join();
16        t2.Join();
17
18        Console.WriteLine(_counter);
19    }
20
21    static void Increment()
22    {
23        for (int i = 0; i < 1000; i++)
24        {
25            Mutex.WaitOne();
26            try
27            {
28                _counter++;
29            }
30            finally
31            {
32                Mutex.ReleaseMutex();
33            }
34        }
35    }
36}

This ensures the increment happens atomically with respect to other threads using the same mutex.

What a Semaphore Is

A semaphore tracks how many threads may enter a protected region at the same time. Instead of capacity one, it has capacity N.

That makes it a good fit for limiting access to a pool of scarce resources such as:

  • database connections
  • outbound HTTP concurrency
  • a fixed number of worker slots
csharp
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
5class Program
6{
7    private static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(2);
8
9    static async Task Main()
10    {
11        var tasks = new Task[5];
12        for (int i = 0; i < tasks.Length; i++)
13        {
14            int id = i;
15            tasks[i] = RunAsync(id);
16        }
17
18        await Task.WhenAll(tasks);
19    }
20
21    static async Task RunAsync(int id)
22    {
23        await Semaphore.WaitAsync();
24        try
25        {
26            Console.WriteLine($"Worker {id} entered");
27            await Task.Delay(500);
28        }
29        finally
30        {
31            Semaphore.Release();
32        }
33    }
34}

Here, two workers can run the protected region at once, but the rest wait.

Which One Should You Use in C#

In modern C#, the first question is often not “mutex or semaphore,” but “do I actually need the Mutex class at all.”

A practical guideline is:

  • use lock or Monitor for simple in-process mutual exclusion
  • use SemaphoreSlim for limiting concurrency inside one process
  • use Mutex when you specifically need cross-process mutual exclusion or named OS-level locking

That distinction matters because Mutex is slower and heavier than the lighter in-process options.

Where You “Need To Implement” Them

You usually do not implement mutexes and semaphores from scratch in application code. You use the synchronization primitives already provided by .NET.

You need them when multiple threads or tasks access shared resources and correctness depends on coordination.

Use a mutex-style primitive when exactly one participant may enter.

Use a semaphore when a small bounded number of participants may enter.

Examples:

  • protecting a shared in-memory counter means mutual exclusion
  • limiting ten simultaneous downloads means a semaphore
  • preventing two desktop app instances from editing the same shared resource may justify a named mutex

Common Pitfalls

The most common mistake is using Mutex when a simple lock would do. That adds overhead without adding value.

Another frequent issue is forgetting to release the primitive in a finally block. That can lead to deadlocks or thread starvation when an exception occurs.

Developers also sometimes use a semaphore to protect code that really requires exclusive ownership. If the resource cannot tolerate concurrent access at all, capacity greater than one is the wrong model.

Finally, be careful mixing thread-based and async code. For async workflows, SemaphoreSlim is usually a better fit than blocking primitives because it supports WaitAsync.

Summary

  • A mutex allows one owner at a time; a semaphore allows a limited number of concurrent owners.
  • In C#, use lock for simple in-process exclusion, SemaphoreSlim for bounded concurrency, and Mutex only when OS-level or cross-process locking is needed.
  • Apply these primitives when shared resources need coordination to stay correct.
  • Always release them reliably, usually in a finally block.
  • Choose the primitive based on the access pattern you need, not on name familiarity.

Course illustration
Course illustration