Чем мьютекс отличается от семафора?
- Что такое монитор?
- Зачем нужна условная переменная?
- Что такое read-write lock?
Мьютекс — замок на одного: в критической секции один поток, отпустить может только владелец. Семафор — счётчик разрешений: одновременно до N потоков.
- Семафор на 1 похож на мьютекс, но у него нет владельца
- Семафор ограничивает параллелизм: не больше 10 запросов к внешнему API
- Монитор = мьютекс + условные переменные (wait/notify, synchronized в Java)
А ещё Read-write lock: много читателей или один писатель
Подробно
Мьютекс (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, а не в цикле.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Мьютекс пускает до N потоков одновременно, семафор — только одного; отпустить мьютекс может любой поток, а семафор — только владелец
- ✗Отпустить мьютекс может любой поток
- ✗Семафор нельзя использовать для ограничения параллелизма
- ✓Мьютекс — один поток в критической секции, отпускает владелец; семафор — счётчик, пускает до N потоков
Что спросят дальше
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.