Получить доступ

Чем мьютекс отличается от семафора?

JuniorКонкурентность
Ещё спрашивают так:
  • Что такое монитор?
  • Зачем нужна условная переменная?
  • Что такое read-write lock?
Коротко

Мьютекс — замок на одного: в критической секции один поток, отпустить может только владелец. Семафор — счётчик разрешений: одновременно до N потоков.

  • Семафор на 1 похож на мьютекс, но у него нет владельца
  • Семафор ограничивает параллелизм: не больше 10 запросов к внешнему API
  • Монитор = мьютекс + условные переменные (wait/notify, synchronized в Java)

А ещё Read-write lock: много читателей или один писатель

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Мьютекс (mutual exclusion) обеспечивает взаимное исключение: захватил — работаешь, остальные ждут; отпустил — один из ждущих заходит. У мьютекса есть владелец: отпустить его должен тот же поток. Бывают реентерабельные — поток может захватить свой же замок повторно (synchronized и ReentrantLock в Java, RLock в Python), иначе рекурсивный вызов заблокирует сам себя.

Семафор — счётчик. acquire уменьшает его и ждёт, если счётчик ноль; release увеличивает и будит ждущего. Владельца нет: освободить может любой поток. Применения:

  • ограничение параллелизма — не больше N одновременных запросов к базе или внешнему API, N соединений в пуле;
  • сигнализация — один поток делает release, другой ждёт на acquire (семафор с нулём);
  • двоичный семафор (N = 1) похож на мьютекс, но из-за отсутствия владельца легко ошибиться.

Монитор — объект, в котором мьютекс объединён с условными переменными: поток в критической секции может ждать условия (wait), временно отпуская замок, а другой — разбудить его (notify). В Java каждый объект — монитор: synchronized + wait/notify; современнее — ReentrantLock + Condition. Ждать условия надо в цикле while (!условие) wait() — бывают ложные пробуждения, и пока поток просыпался, условие мог перехватить другой.

Read-write lock — много читателей одновременно или один писатель (ReentrantReadWriteLock). Выгоден, когда чтений намного больше и критическая секция заметная; иначе накладные расходы съедают выигрыш. StampedLock добавляет оптимистичное чтение без блокировки.

Цена. Свободный замок захватывается атомарной инструкцией за десятки наносекунд. Занятый — поток засыпает и просыпается через ядро (переключение контекста), это микросекунды. Поэтому критические секции делают короткими и не делают внутри них ввод-вывод.

Пример

import threading, time

api_slots = threading.Semaphore(3)      # не больше 3 одновременных вызовов

def call_api(i):
    with api_slots:
        print("старт", i)
        time.sleep(1)                   # одновременно спят максимум трое

for i in range(9):
    threading.Thread(target=call_api, args=(i,)).start()

Что хотят услышать

  • Мьютекс — один поток и владелец, семафор — счётчик без владельца.
  • Применение семафора для ограничения параллелизма.
  • Монитор и условные переменные, ожидание в цикле.
  • Read-write lock и когда он оправдан — плюс.

Частые ошибки

  • Говорить, что семафор — «мьютекс на несколько потоков» и не упоминать отсутствие владельца.
  • Вызывать wait через if, а не в цикле.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем