Ответ
Semaphore и SemaphoreSlim — это примитивы синхронизации, ограничивающие число потоков, которые могут одновременно получить доступ к ресурсу или пулу. Их ключевое отличие — в области применения и производительности.
SemaphoreSlim (рекомендуется для внутрипроцессной синхронизации)
- Назначение: Легковесная версия, оптимизированная для сценариев в рамках одного процесса.
- Производительность: Высокая, так как реализована в пользовательском режиме (user-mode) и обращается к ядру ОС только при блокировке.
- Ключевая особенность: Поддерживает асинхронное ожидание через метод
WaitAsync(), что критически важно для современных async/await паттернов. - Межпроцессность: Не поддерживает именованные семафоры, работает только внутри процесса.
// Пример: ограничение параллельных HTTP-запросов
private static readonly SemaphoreSlim _throttler = new SemaphoreSlim(5); // Макс 5 параллельных задач
public async Task<string> MakeThrottledRequestAsync(string url)
{
await _throttler.WaitAsync(); // Асинхронное ожидание разрешения
try
{
using var client = new HttpClient();
return await client.GetStringAsync(url);
}
finally
{
_throttler.Release(); // Освобождение слота
}
}
Semaphore (для сложных сценариев)
- Назначение: Класс-обёртка над объектом ядра ОС Windows. Используется для межпроцессной синхронизации или когда нужны функции, специфичные для ядра.
- Производительность: Ниже, чем у
SemaphoreSlim, так как каждый вызовWaitOne()/Release()требует переключения в режим ядра. - Ключевая особенность: Поддерживает именованные семафоры, которые могут синхронизировать потоки разных процессов.
- Асинхронность: Не имеет нативных асинхронных методов.
// Пример: межпроцессная синхронизация доступа к общему файлу
// Имя семафора должно быть уникальным в системе
using var semaphore = new Semaphore(1, 1, "Global\MyApp_FileAccessSemaphore");
semaphore.WaitOne(); // Синхронная блокировка (может быть долгой)
try
{
// Критическая секция, доступная только одному процессу за раз
File.AppendAllText("shared.log", $"{DateTime.UtcNow}: Запись из процесса {Process.GetCurrentProcess().Id}n");
}
finally
### Практическое правило
* **Всегда начинайте с `SemaphoreSlim`**, если синхронизация нужна только внутри вашего приложения.
* Используйте `Semaphore` только в двух случаях:
1. Требуется синхронизация между несколькими независимыми процессами.
2. Необходимо время ожидания, превышающее возможности `SemaphoreSlim` (что маловероятно).