Sobes.tech
Назад к вопросам
Junior — Middle
58

Чем отличаются механизм взаимного исключения Lock и Semaphore при управлении доступом к ресурсам?

Компании, где спрашивали
appfox

Ответ от нейросети

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() возвращает разрешение обратно, поэтому другой поток сможет войти позже.

По шагам это работает так:

  1. Создаётся объект синхронизации.
  2. Поток запрашивает доступ через lock или WaitOne().
  3. Если доступ уже занят, поток ждёт.
  4. После завершения работы ресурс освобождается через выход из lock или Release().

Ключевые моменты:

  • lock обеспечивает взаимное исключение: одновременно внутри только один поток.
  • Semaphore ограничивает количество одновременно работающих потоков, а не только одного.
  • lock обычно проще, короче и безопаснее для защиты общей памяти внутри процесса.
  • Semaphore применяют для управления ограниченными ресурсами: подключениями, запросами, пулами.
  • С Semaphore важно всегда вызывать Release(), обычно в finally, иначе можно "заблокировать" разрешения.
  • Для синхронизации внутри одного процесса lock чаще предпочтительнее; Semaphore нужен, когда требуется допустить несколько одновременных участников.