Чем отличаются механизм взаимного исключения Lock и Semaphore при управлении доступом к ресурсам?
Ответ от нейросети
sobes.tech AI
Что хотят услышать интервьюеры:
Lock обычно используют для защиты критической секции в одном процессе, когда доступ должен быть только у одного потока. Semaphore — более общий механизм, который ограничивает число одновременно работающих потоков до заданного лимита. Важно понимать, что lock проще и безопаснее для взаимного исключения, а Semaphore подходит для контроля пула ресурсов.
Определение:
lock в C# — это удобная конструкция для взаимного исключения: в один момент времени только один поток может войти в защищённый блок кода.
Semaphore — это счётчик разрешений: он позволяет одновременно войти не одному, а нескольким потокам, если число доступных "слотов" больше нуля.
Если говорить практично:
lock— "вход только одному";Semaphore— "вход ограниченному количеству".
Пример использования:
lock подходит, когда несколько потоков пишут в общий список или изменяют один и тот же объект.
Semaphore полезен, когда есть ограниченный ресурс, например, максимум 3 одновременных запроса к внешнему API.
using System;
using System.Threading;
class Program
{
private static readonly object _lockObj = new object();
private static int _counter = 0;
static void Main()
{
// lock: только один поток заходит в критическую секцию
lock (_lockObj)
{
_counter++;
Console.WriteLine($"Counter = {_counter}");
}
// Semaphore: разрешаем до 3 одновременных входов
using var semaphore = new Semaphore(3, 3);
semaphore.WaitOne();
try
{
Console.WriteLine("Поток получил разрешение на работу с ресурсом");
}
finally
{
semaphore.Release();
}
}
}
Пояснение кода:
В первом блоке lock гарантирует, что инкремент _counter выполнится атомарно относительно других потоков. Если несколько потоков попытаются войти одновременно, они будут ждать освобождения монитора.
Во втором блоке Semaphore(3, 3) создаёт семафор с тремя разрешениями. WaitOne() уменьшает счётчик разрешений и блокирует поток, если разрешения закончились. Release() возвращает разрешение обратно, поэтому другой поток сможет войти позже.
По шагам это работает так:
- Создаётся объект синхронизации.
- Поток запрашивает доступ через
lockилиWaitOne(). - Если доступ уже занят, поток ждёт.
- После завершения работы ресурс освобождается через выход из
lockилиRelease().
Ключевые моменты:
lockобеспечивает взаимное исключение: одновременно внутри только один поток.Semaphoreограничивает количество одновременно работающих потоков, а не только одного.lockобычно проще, короче и безопаснее для защиты общей памяти внутри процесса.Semaphoreприменяют для управления ограниченными ресурсами: подключениями, запросами, пулами.- С
Semaphoreважно всегда вызыватьRelease(), обычно вfinally, иначе можно "заблокировать" разрешения. - Для синхронизации внутри одного процесса
lockчаще предпочтительнее;Semaphoreнужен, когда требуется допустить несколько одновременных участников.