> Что такое дедлоки? (Python)
Уровень: junior · Роль: backend · Язык: Python · Категория: Технические вопросы
Компании: HeadHunter
Стек: Python
> Пример ответа
Короткий ответ
Дедлок - это ситуация в многопоточном или многопроцессном приложении, когда два или более потока блокируют друг друга, ожидая ресурсы, которые удерживают другие потоки, и ни один не может продолжить выполнение. В результате программа зависает навсегда. В Python дедлоки чаще всего возникают при использовании threading.Lock или asyncio.Lock, когда порядок захвата блокировок не согласован.
Подробное объяснение
Дедлок возникает при выполнении четырёх условий Коффмана:
- Взаимное исключение - ресурс может быть занят только одним потоком одновременно.
- Удержание и ожидание - поток удерживает один ресурс и ждёт другой.
- Невытесняемость - ресурс нельзя принудительно забрать у потока.
- Циклическое ожидание - существует цикл потоков, где каждый ждёт ресурс, удерживаемый следующим.
Классический пример - два потока, каждый захватывает свою блокировку, а затем пытается захватить блокировку другого. Если оба сделают это одновременно, они будут ждать вечно.
В Python дедлоки особенно опасны, потому что GIL не защищает от них - GIL управляет только выполнением байткода, а не логикой блокировок. Дедлок может произойти и в asyncio, если корутина ждёт событие, которое никогда не наступит.
На практике
На практике дедлоки проявляются как зависание программы без ошибок и без потребления CPU. Их сложно отлаживать, потому что они не всегда воспроизводятся - зависят от тайминга потоков.
Основные способы борьбы:
- Фиксированный порядок захвата блокировок - всегда захватывать блокировки в одном и том же порядке.
- Таймауты - использовать
lock.acquire(timeout=...), чтобы не ждать вечно. - Использование
contextlib- захватывать несколько блокировок черезwithв правильном порядке. - Избегать вложенных блокировок - если возможно, перепроектировать код.
- Использовать
threading.RLock- если нужна повторная блокировка тем же потоком.
В Python также есть threading.Lock и threading.RLock, а для asyncio - asyncio.Lock. Важно помнить, что asyncio.Lock не потокобезопасен, его нельзя использовать из разных потоков.
Пример кода
PYTHONimport threadingimport timelock_a = threading.Lock()lock_b = threading.Lock()def worker_1():with lock_a:print("worker_1: захватил lock_a")time.sleep(0.1)with lock_b:print("worker_1: захватил lock_b")def worker_2():with lock_b:print("worker_2: захватил lock_b")time.sleep(0.1)with lock_a:print("worker_2: захватил lock_a")t1 = threading.Thread(target=worker_1)t2 = threading.Thread(target=worker_2)t1.start()t2.start()t1.join()t2.join()
Здесь оба потока захватывают блокировки в разном порядке, что приводит к дедлоку. Исправление - захватывать блокировки в одном порядке в обоих потоках.
Как отвечать на собеседовании
Начни с определения дедлока и перечисли четыре условия Коффмана. Затем приведи простой пример с двумя блокировками. Объясни, почему это проблема именно для Python - GIL не спасает. Упомяни способы предотвращения: фиксированный порядок, таймауты, минимизация вложенных блокировок. Если спросят про asyncio - скажи, что там тоже возможны дедлоки, но механизм другой.
Что проверяет интервьюер
Интервьюер проверяет:
- понимание базовых концепций многопоточности;
- знание условий возникновения дедлока;
- умение анализировать код на предмет потенциальных дедлоков;
- знание практических методов предотвращения;
- понимание особенностей Python (GIL, asyncio).
Типичные ошибки
- Путать дедлок с гонкой данных или livelock.
- Думать, что GIL предотвращает дедлоки.
- Не упоминать условия Коффмана.
- Предлагать только один способ решения, например, только таймауты.
- Не учитывать, что дедлок может быть не только с
threading.Lock, но и с другими примитивами синхронизации.
> Похожие задачи по Python
Как работает периодический таск, который читает таблицу уведомлений и отправляет их?
Какие решения кроме паттерна outbox помогают гарантировать выполнение асинхронных задач?
Что является верхнеуровневым и низкоуровневым компонентом в системах на SOLID?
Как обойти блокировку при вызове синхронной функции в асинхронном коде в Python?
> Похожие задачи по backend
Как работает периодический таск, который читает таблицу уведомлений и отправляет их?
Какие решения кроме паттерна outbox помогают гарантировать выполнение асинхронных задач?
Что является верхнеуровневым и низкоуровневым компонентом в системах на SOLID?
Как обойти блокировку при вызове синхронной функции в асинхронном коде в Python?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью