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

Объясните отличия между механизмами блокировки: lock, Semaphore и Mutex.

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

sobes.tech AI

Что хотят услышать интервьюеры:

lock — это самый простой способ сделать критическую секцию в C#: он гарантирует, что код внутри выполнит только один поток одновременно. Mutex и Semaphore — более низкоуровневые примитивы синхронизации; Mutex обычно нужен для взаимного исключения, в том числе между процессами, а Semaphore — для ограничения числа одновременно работающих потоков. Важно понимать не только назначение, но и стоимость: lock обычно легче и быстрее, чем Mutex.

Определение:

lock — это синтаксический сахар над монитором (Monitor) в C#, который защищает участок кода от одновременного доступа несколькими потоками внутри одного процесса.

Mutex — это объект взаимного исключения, который может синхронизировать доступ как внутри одного процесса, так и между процессами. В каждый момент времени владельцем может быть только один поток.

Semaphore — это счётчик разрешений. Он позволяет одновременно войти в критическую секцию не одному, а ограниченному числу потоков. Когда разрешения заканчиваются, остальные потоки ждут.

Пример использования:

using System;
using System.Threading;

class Program
{
    private static readonly object _lockObj = new object();
    private static int _counter = 0;

    private static readonly Mutex _mutex = new Mutex();
    private static readonly Semaphore _semaphore = new Semaphore(2, 2);

    static void Main()
    {
        // lock: только один поток внутри критической секции
        lock (_lockObj)
        {
            _counter++;
        }

        // Mutex: взаимное исключение, можно использовать между процессами
        _mutex.WaitOne();
        try
        {
            _counter++;
        }
        finally
        {
            _mutex.ReleaseMutex();
        }

        // Semaphore: одновременно не более 2 потоков
        _semaphore.WaitOne();
        try
        {
            Console.WriteLine("Поток вошёл в ограниченную секцию");
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

Пояснение кода:

  • lock (_lockObj) защищает изменение _counter так, чтобы в этот блок одновременно вошёл только один поток.
  • Mutex требует явного захвата через WaitOne() и освобождения через ReleaseMutex(). Если не освободить в finally, можно получить зависание.
  • Semaphore(2, 2) означает, что одновременно в секцию могут войти максимум 2 потока.
  • Для Semaphore после работы обязательно вызывается Release(), иначе разрешение не вернётся в счётчик.
  • На практике lock чаще всего используют для синхронизации внутри приложения, а Mutex — когда нужна межпроцессная синхронизация, Semaphore — когда нужен лимит параллелизма.

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

  • lock — самый удобный и распространённый вариант для синхронизации внутри одного процесса.
  • Mutex — взаимное исключение с возможностью межпроцессной синхронизации, обычно тяжелее lock.
  • Semaphore — не “один владелец”, а ограничение количества одновременных входов.
  • Для коротких критических секций обычно выбирают lock.
  • Любой из этих механизмов нужно освобождать корректно, иначе легко получить дедлок или утечку разрешений.
  • Нельзя использовать их как замену друг другу без понимания задачи: выбор зависит от того, нужен один поток, несколько потоков или синхронизация между процессами.