Если T1 захватил A, а T2 — B, оба могут бесконечно ждать второй mutex. Единый порядок захвата блокировок по неизменяемому уникальному id кошелька устраняет этот цикл.
Почему встречные переводы Wallet могут вызвать deadlock?
Если 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 в каждом запуске, но описанное взаимное ожидание делает его возможным.