Выбор ответа Middle Многопоточность и модель памяти спрашивают часто обновлено
Как избежать дедлока с двумя мьютексами
Вопрос
Функция переводит деньги между двумя счетами, у каждого свой мьютекс. Как захватить оба без риска взаимной блокировки?
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Правильный ответ: std::scopedlock lock(a.m, b.m); или единый глобальный порядок захвата. Перевод a→b в одном потоке и b→a в другом при порядке «отправитель, получатель» даст дедлок. std::scopedlock (C++17) захватывает несколько мьютексов алгоритмом избегания взаимной блокировки (как std::lock), альтернатива: единый порядок,…
Варианты ответа
- Захватывать сначала мьютекс счёта-отправителя, потом получателя
std::scoped_lock lock(a.m, b.m);или единый глобальный порядок захватаправильный ответ- Заменить оба мьютекса на
std::recursive_mutex, он не блокируется - Вызвать
lock()у обоих в любом порядке, а потомunlock()
Разбор
Перевод a→b в одном потоке и b→a в другом при порядке «отправитель, получатель» даст дедлок. std::scoped_lock (C++17) захватывает несколько мьютексов алгоритмом избегания взаимной блокировки (как std::lock), альтернатива: единый порядок, например по адресу или id счёта.
- Порядок «отправитель первым» как раз и создаёт встречные захваты.
- Рекурсивный мьютекс помогает только при повторном захвате тем же потоком.
- Произвольный порядок это и есть причина дедлока.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Ещё по теме «Многопоточность и модель памяти»
- Найти потенциальный дедлок по порядку захвата мьютексов
- Пул потоков: кто и когда выполнит задачу
- Публикация данных через флаг
- Ловушка std::async
- Потокобезопасная ленивая инициализация
- compare_exchange
- Ожидание на condition_variable
- Примитив синхронизации и задача