Что такое deadlock в многопоточности
Что проверяют
Умеете ли вы рассуждать о разделяемых ресурсах, а не только звать threading.Lock. В Python тема кажется неактуальной из-за GIL, и интервьюер часто целится именно в это заблуждение: GIL сериализует байткод, но двум потокам взаимно заблокироваться на пользовательских блокировках не мешает.
Как отвечать
Взаимоблокировка это состояние, когда группа потоков ждёт друг друга по кругу и ни один не может продолжить. Классических условий четыре: взаимное исключение, удержание ресурса с ожиданием следующего, невозможность отобрать ресурс силой и циклическое ожидание. Ломаем любое из них.
Практика Python, о которой стоит сказать:
- единый порядок захвата блокировок во всём коде, он убирает цикл ожидания;
acquire(timeout=...)вместо бесконечного ожидания: зависание превращается в понятную ошибку;with lock:вместо парыacquire/release, иначе исключение оставит блокировку захваченной;RLock, если поток входит в свой же критический участок повторно: обычныйLockне реентерабельный, и поток блокирует сам себя;- в
concurrent.futuresнельзя ждать результат задачи, отправленной в тот же пул из его воркера.
Диагностика: встроенного детектора в языке нет, живой процесс смотрят через faulthandler.dump_traceback_later() или внешним профилировщиком стека.
Где спотыкаются
Отвечают, что при наличии GIL взаимоблокировка невозможна. GIL отдаётся на любой блокирующей операции, и два потока спокойно встают друг за другом. Заодно путают взаимоблокировку с голоданием, где поток работает, но не продвигается.
Сужают тему до потоков. Однопоточный код тоже встаёт: корутина держит asyncio.Lock и ждёт другую корутину, которая ждёт эту. Отдельная классика это порождение процесса через fork в приложении с потоками: ребёнок наследует захваченную кем-то блокировку и замирает на первом же логировании.
Лечат симптом таймаутом. Он превращает зависание в ошибку, но цикл ожидания остаётся, и под нагрузкой всё вернётся. Убирают причину: общий порядок захвата, сокращение критической секции, одна транзакция вместо двух вложенных. На стороне базы взаимоблокировку разрывает сама СУБД, и от приложения нужен повтор транзакции, а не молчаливое падение.