ConcurrencyC#verifiedVerified

Semaphore Pattern in C#

Control access to a finite pool of resources by maintaining a counter that threads atomically increment (release) and decrement (acquire), blocking when the count reaches zero.

How to Implement the Semaphore Pattern in C#

1Step 1: Use SemaphoreSlim to limit concurrent access

public class ConnectionPool
{
    private readonly SemaphoreSlim _semaphore;
    private readonly int _maxConnections;

    public ConnectionPool(int maxConnections)
    {
        _maxConnections = maxConnections;
        _semaphore = new SemaphoreSlim(maxConnections, maxConnections);
    }

2Step 2: Acquire a connection (blocks if pool exhausted)

    public async Task<string> AcquireAsync()
    {
        await _semaphore.WaitAsync();
        return $"Connection (available: {_semaphore.CurrentCount}/{_maxConnections})";
    }

3Step 3: Release the connection back to the pool

    public void Release()
    {
        _semaphore.Release();
    }
}

// Usage:
// var pool = new ConnectionPool(3);
// var tasks = Enumerable.Range(0, 10).Select(async i =>
// {
//     var conn = await pool.AcquireAsync();
//     Console.WriteLine($"Task {i}: {conn}");
//     await Task.Delay(100);
//     pool.Release();
// });
// await Task.WhenAll(tasks);

Semaphore Pattern Architecture

hourglass_empty

Rendering diagram...

lightbulb

Semaphore Pattern in the Real World

Imagine a car park with exactly three spaces. A ticket machine at the entrance (the semaphore) issues a ticket only if spaces remain, lifting the barrier; arriving drivers with no ticket available must wait. When a car exits, the machine automatically increments its counter and releases the next waiting driver — the car park never exceeds capacity.