Что такое SemaphoreSlim и чем он отличается от Semaphore? Приведите пример использования.?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
SemaphoreSlim vs Semaphore: общая концепция
Оба класса реализуют семафор — примитив синхронизации для управления доступом к ограниченному количеству ресурсов. Семафор поддерживает счётчик, указывающий количество доступных разрешений. Потоки могут захватывать (wait) разрешение (уменьшая счётчик) и освобождать (release) его (увеличивая счётчик). Если счётчик равен нулю, поток блокируется до появления свободного разрешения.
Ключевые различия
| Аспект | Semaphore | SemaphoreSlim |
|---|---|---|
| Пространство имён | System.Threading | System.Threading |
| Тип семафора | Ядерный объект ОС (межпроцессный). | User-mode с быстрым путем, резервно использует ядерные объекты. |
| Производительность | Медленнее, требует переключения в режим ядра. | Быстрее, особенно когда нет конкуренции. |
| Именование | Поддерживает именованные семафоры для синхронизации между процессами. | Только для синхронизации в рамках одного процесса. |
| Асинхронная поддержка | Нет встроенной поддержки async/await. | Есть метод WaitAsync() для асинхронного ожидания. |
| Максимальное значение | Задаётся при создании (до int.MaxValue). | int.MaxValue, но рекомендуется для небольшого числа параллельных операций. |
| Дополнительные функции | — | Методы CurrentCount, AvailableWaitHandle (с осторожностью). |
Основной вывод: SemaphoreSlim — это облегчённая, высокопроизводительная версия для синхронизации потоков внутри одного процесса с поддержкой асинхронности. Semaphore используется реже, в основном для межпроцессной синхронизации.
Пример использования SemaphoreSlim
Типичный сценарий — ограничение количества одновременных запросов к внешнему API или базы данных.
using System;
using System.Threading;
using System.Threading.Tasks;
public class RateLimitedApiClient
{
// Семафор, разрешающий не более 3 одновременных вызовов
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3, 3);
public async Task<string> CallApiAsync(int requestId)
{
// Асинхронно ждём разрешения (не блокируя поток)
await _semaphore.WaitAsync();
try
{
Console.WriteLine($"Запрос {requestId} начал выполнение. Доступно слотов: {_semaphore.CurrentCount}");
// Имитация долгой работы (например, HTTP-запрос)
await Task.Delay(2000);
Console.WriteLine($"Запрос {requestId} завершён.");
return $"Результат для {requestId}";
}
finally
{
// Всегда освобождаем семафор!
_semaphore.Release();
}
}
}
public class Program
{
public static async Task Main()
{
var client = new RateLimitedApiClient();
var tasks = new Task<string>[10];
// Запускаем 10 "параллельных" задач
for (int i = 0; i < 10; i++)
{
tasks[i] = client.CallApiAsync(i);
}
// Ждём завершения всех
var results = await Task.WhenAll(tasks);
Console.WriteLine("Все запросы обработаны.");
}
}
Пояснение к примеру:
new SemaphoreSlim(3, 3)— создаёт семафор с начальным и максимальным значением счётчика 3.WaitAsync()— асинхронно ожидает доступного разрешения. ЕслиCurrentCount > 0, поток продолжает выполнение без блокировки.CurrentCount— свойство, показывающее текущее количество доступных разрешений (для мониторинга/отладки).Release()— освобождает разрешение, увеличивая счётчик. Критично вызывать вfinally-блоке, чтобы избежать утечки семафора (ситуации, когда разрешение никогда не вернётся).
Важные нюансы:
- Не используйте
AvailableWaitHandleуSemaphoreSlimбез крайней необходимости — это ядерный объект, создание которого снижает производительность. - Для синхронного ожидания используйте
Wait(), но в асинхронном коде предпочтительнееWaitAsync(). SemaphoreSlimне потокобезопасен дляDispose()— убедитесь, что никто не использует семафор при вызовеDispose().- В отличие от
Semaphore,SemaphoreSlimне позволяет задавать имя и не может использоваться для синхронизации между процессами.
Когда выбирать:
SemaphoreSlim— в 95% случаев для ограничения параллелизма внутри приложения (запросы, операции ввода-вывода, работа с пулом).Semaphore— только когда требуется синхронизация нескольких независимых процессов (например, два разных .NET-приложения, работающих с общим ресурсом).