← Назад к вопросам

Что такое Semaphore?

1.8 Middle🔥 61 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Semaphore в C#

Semaphore — это синхронизационный примитив, который ограничивает количество потоков, имеющих доступ к ресурсу. Это более гибкая альтернатива Lock для управления доступом к пулу ресурсов.

Как работает Semaphore

// Максимум 3 потока могут одновременно использовать ресурс
var semaphore = new Semaphore(3, 3);

for (int i = 0; i < 10; i++)
{
    int taskId = i;
    Task.Run(() =>
    {
        semaphore.WaitOne(); // Ждем, пока счетчик > 0
        try
        {
            Console.WriteLine($"Task {taskId} started");
            Thread.Sleep(1000);
            Console.WriteLine($"Task {taskId} finished");
        }
        finally
        {
            semaphore.Release(); // Увеличиваем счетчик
        }
    });
}

Binary Semaphore vs Counting Semaphore

Binary Semaphore (0 или 1) — как Lock:

var binary = new Semaphore(1, 1); // 1 поток

Counting Semaphore (n ресурсов):

var counting = new Semaphore(5, 5); // 5 потоков

Named Semaphore (inter-process)

// Разные приложения могут синхронизироваться
var named = new Semaphore(2, 2, "MyAppResource");

SemaphoreSlim для асинхронного кода

private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3); // 3 одновременно

public async Task ProcessAsync(int id)
{
    await _semaphore.WaitAsync(); // Асинхронное ожидание
    try
    {
        Console.WriteLine($"Processing {id}");
        await Task.Delay(1000);
    }
    finally
    {
        _semaphore.Release();
    }
}

// Использование
var tasks = Enumerable.Range(1, 10)
    .Select(i => ProcessAsync(i))
    .ToArray();
await Task.WhenAll(tasks);

Пример: Пул соединений

public class ConnectionPool
{
    private readonly SemaphoreSlim _available;
    private readonly List<DbConnection> _connections;

    public ConnectionPool(int size)
    {
        _available = new SemaphoreSlim(size);
        _connections = new List<DbConnection>(size);
        for (int i = 0; i < size; i++)
            _connections.Add(new DbConnection());
    }

    public async Task<DbConnection> GetConnectionAsync()
    {
        await _available.WaitAsync();
        lock (_connections)
        {
            return _connections[0];
        }
    }

    public void ReturnConnection(DbConnection conn)
    {
        _available.Release();
    }
}

Semaphore vs Lock

АспектSemaphoreLock
Количество потоковМного (N)1
АсинхронностьSemaphoreSlimНет
Inter-processДа (Named)Нет
СложностьВышеНиже
ИспользованиеПулы ресурсовКритические секции

Проблемы и решения

Deadlock при неправильном Release:

var sem = new Semaphore(1, 1);
sem.WaitOne();
// Забыли Release() — deadlock!
// sem.Release(); // ← ОБЯЗАТЕЛЬНО!

Правильно с try-finally:

var sem = new Semaphore(2, 2);
sem.WaitOne();
try
{
    // Работа с ресурсом
}
finally
{
    sem.Release(); // Гарантированно вызовется
}

Практические применения

  • Пулы соединений — ограничение одновременных подключений
  • Rate limiting — ограничение запросов в секунду
  • Управление ресурсами — ограничение потребления памяти
  • Обработка очередей — контроль параллельных задач

Semaphore — это мощный инструмент для управления доступом к ограниченным ресурсам в многопоточных приложениях.

Что такое Semaphore? | PrepBro