Почему встречные переводы Wallet могут вызвать deadlock?

Если T1 захватил A, а T2 — B, оба могут бесконечно ждать второй mutex. Единый порядок захвата блокировок по неизменяемому уникальному id кошелька устраняет этот цикл.

Короткий ответ

Что ответить на собеседовании

Если T1 захватил A, а T2 — B, оба могут бесконечно ждать второй mutex. Единый порядок захвата блокировок по неизменяемому уникальному id кошелька устраняет этот цикл.

Подробный разбор

Ответ с пояснениями

Условие

В Wallet.transfer сначала блокируется mutex отправителя, затем mutex получателя; обе блокировки удерживаются до завершения изменения балансов. Поток T1 переводит из A в B, T2 — из B в A. Блокировки невытесняемые и не имеют timeout. Объясните возможное зависание и исправление.

Ответ

Если T1 захватил A, а T2 — B, оба могут бесконечно ждать второй mutex. Единый порядок захвата блокировок по неизменяемому уникальному id кошелька устраняет этот цикл.

Оба перевода сначала блокируют кошелёк с меньшим id, затем с большим, независимо от направления перевода. Проверка остатка и изменение обоих балансов выполняются внутри этой общей критической секции, освобождение — гарантированно при выходе. Перевод самому себе обрабатывают отдельно, чтобы не захватывать один нерекурсивный mutex дважды. Последовательность запуска потоков не доказывает наличие deadlock в каждом запуске, но описанное взаимное ожидание делает его возможным.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее