Выбор ответа Middle Многопоточность и модель памяти спрашивают часто обновлено

Как избежать дедлока с двумя мьютексами

Вопрос

Функция переводит деньги между двумя счетами, у каждого свой мьютекс. Как захватить оба без риска взаимной блокировки?

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: std::scopedlock lock(a.m, b.m); или единый глобальный порядок захвата. Перевод a→b в одном потоке и b→a в другом при порядке «отправитель, получатель» даст дедлок. std::scopedlock (C++17) захватывает несколько мьютексов алгоритмом избегания взаимной блокировки (как std::lock), альтернатива: единый порядок,…

Варианты ответа

  1. Захватывать сначала мьютекс счёта-отправителя, потом получателя
  2. std::scoped_lock lock(a.m, b.m); или единый глобальный порядок захватаправильный ответ
  3. Заменить оба мьютекса на std::recursive_mutex, он не блокируется
  4. Вызвать lock() у обоих в любом порядке, а потом unlock()

Разбор

Перевод a→b в одном потоке и b→a в другом при порядке «отправитель, получатель» даст дедлок. std::scoped_lock (C++17) захватывает несколько мьютексов алгоритмом избегания взаимной блокировки (как std::lock), альтернатива: единый порядок, например по адресу или id счёта.

  • Порядок «отправитель первым» как раз и создаёт встречные захваты.
  • Рекурсивный мьютекс помогает только при повторном захвате тем же потоком.
  • Произвольный порядок это и есть причина дедлока.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.