Напишите псевдокод многопоточной программы, где происходит дедлок при использовании двух мьютексов m1 и m2; объясните причины дедлока и предложите стратегии предотвращения (упорядочивание, trylock, время ожидания, обнаружение и восстановление).
Псевдокод, вызывающий дедлок (два потока захватывают мьютексы в разном порядке): Пусть мьютексы m1m_1m1, m2m_2m2; потоки T1T_1T1, T2T_2T2. T1: lock(m1m_1m1) // делает часть работы lock(m2m_2m2) // критическая секция unlock(m2m_2m2) unlock(m1m_1m1) T2: lock(m2m_2m2) // делает часть работы lock(m1m_1m1) // критическая секция unlock(m1m_1m1) unlock(m2m_2m2) Причина дедлока (Coffman conditions): - Взаимное исключение (mutual exclusion): каждый мьютекс эксклюзивен. - Удержание и ожидание (hold-and-wait): T1T_1T1 держит m1m_1m1 и ждёт m2m_2m2; T2T_2T2 держит m2m_2m2 и ждёт m1m_1m1. - Непрерываемость (no preemption): мьютексы не могут быть принудительно отобраны. - Кольцевая блокировка (circular wait): T1→m2→T2→m1→T1T_1 \rightarrow m_2 \rightarrow T_2 \rightarrow m_1 \rightarrow T_1T1→m2→T2→m1→T1. Стратегии предотвращения и примеры (плюсы/минусы коротко): 1) Упорядочивание захвата (static ordering) - Правило: каждый поток должен захватывать мьютексы в согласованном порядке, например m1m_1m1 перед m2m_2m2. Псевдокод: Любой поток, нуждающийся в обоих: lock(m1m_1m1) lock(m2m_2m2) ... unlock(m2m_2m2) unlock(m1m_1m1) - Плюсы: просто, эффективно; устраняет circular wait. - Минусы: требует глобальной политики, не всегда применимо при динамическом наборе ресурсов. 2) trylock с откатом и экспоненциальным бэкоффом Псевдокод (для двух мьютексов): while true: lock(m1m_1m1) if try_lock(m2m_2m2): break else: unlock(m1m_1m1) sleep(backoff()) // повторить // критическая секция unlock(m2m_2m2) unlock(m1m_1m1) - Плюсы: избегает блокировки навсегда; прост в реализации. - Минусы: возможны лишние повторные попытки и живучесть (livelock) без правильного бэкоффа. 3) Таймаут при блокировке (timed lock) Псевдокод: if timed_lock(m1m_1m1, ttt): if timed_lock(m2m_2m2, ttt): // критическая секция unlock(m2m_2m2) unlock(m1m_1m1) else: unlock(m1m_1m1) // обработка неудачи (повторить/ошибка) else: // обработка неудачи - Плюсы: даёт гарантию возврата в случае ожидания; гибче. - Минусы: нужно выбирать ttt и обрабатывать откаты; может усложнить логику. 4) Обнаружение и восстановление (deadlock detection & recovery) - Идея: позволить ресурсы захватываться, периодически строить wait-for граф; при обнаружении цикла — выбрать жертву (roll back, принудительно прервать поток или отнять ресурсы) и освободить ресурсы. Псевдокод мониторинга: periodically: build_wait_for_graph() if cycle_detected(graph): victim = select_victim(cycle) // критерии: минимальная стоимость отката terminate_or_rollback(victim) - Плюсы: применимо при динамичных ресурсах. - Минусы: сложность детекции, стоимость восстановления, возможна потеря работы. Короткие рекомендации: - Если можно задать глобальный порядок — используйте его (самый простой и быстрый). - Для динамики — trylock/timeout с бэкоффом. - В системах с критичной надежностью — детекция и восстановление (или транзакции с откатом). - Комбинируйте подходы: порядок для большинства случаев + таймаут/trylock для редких динамических ситуаций.
Пусть мьютексы m1m_1m1 , m2m_2m2 ; потоки T1T_1T1 , T2T_2T2 .
T1:
lock(m1m_1m1 )
// делает часть работы
lock(m2m_2m2 )
// критическая секция
unlock(m2m_2m2 )
unlock(m1m_1m1 )
T2:
lock(m2m_2m2 )
// делает часть работы
lock(m1m_1m1 )
// критическая секция
unlock(m1m_1m1 )
unlock(m2m_2m2 )
Причина дедлока (Coffman conditions):
- Взаимное исключение (mutual exclusion): каждый мьютекс эксклюзивен.
- Удержание и ожидание (hold-and-wait): T1T_1T1 держит m1m_1m1 и ждёт m2m_2m2 ; T2T_2T2 держит m2m_2m2 и ждёт m1m_1m1 .
- Непрерываемость (no preemption): мьютексы не могут быть принудительно отобраны.
- Кольцевая блокировка (circular wait): T1→m2→T2→m1→T1T_1 \rightarrow m_2 \rightarrow T_2 \rightarrow m_1 \rightarrow T_1T1 →m2 →T2 →m1 →T1 .
Стратегии предотвращения и примеры (плюсы/минусы коротко):
1) Упорядочивание захвата (static ordering)
- Правило: каждый поток должен захватывать мьютексы в согласованном порядке, например m1m_1m1 перед m2m_2m2 .
Псевдокод:
Любой поток, нуждающийся в обоих:
lock(m1m_1m1 )
lock(m2m_2m2 )
...
unlock(m2m_2m2 )
unlock(m1m_1m1 )
- Плюсы: просто, эффективно; устраняет circular wait.
- Минусы: требует глобальной политики, не всегда применимо при динамическом наборе ресурсов.
2) trylock с откатом и экспоненциальным бэкоффом
Псевдокод (для двух мьютексов):
while true:
lock(m1m_1m1 )
if try_lock(m2m_2m2 ):
break
else:
unlock(m1m_1m1 )
sleep(backoff())
// повторить
// критическая секция
unlock(m2m_2m2 )
unlock(m1m_1m1 )
- Плюсы: избегает блокировки навсегда; прост в реализации.
- Минусы: возможны лишние повторные попытки и живучесть (livelock) без правильного бэкоффа.
3) Таймаут при блокировке (timed lock)
Псевдокод:
if timed_lock(m1m_1m1 , ttt):
if timed_lock(m2m_2m2 , ttt):
// критическая секция
unlock(m2m_2m2 )
unlock(m1m_1m1 )
else:
unlock(m1m_1m1 )
// обработка неудачи (повторить/ошибка)
else:
// обработка неудачи
- Плюсы: даёт гарантию возврата в случае ожидания; гибче.
- Минусы: нужно выбирать ttt и обрабатывать откаты; может усложнить логику.
4) Обнаружение и восстановление (deadlock detection & recovery)
- Идея: позволить ресурсы захватываться, периодически строить wait-for граф; при обнаружении цикла — выбрать жертву (roll back, принудительно прервать поток или отнять ресурсы) и освободить ресурсы.
Псевдокод мониторинга:
periodically:
build_wait_for_graph()
if cycle_detected(graph):
victim = select_victim(cycle) // критерии: минимальная стоимость отката
terminate_or_rollback(victim)
- Плюсы: применимо при динамичных ресурсах.
- Минусы: сложность детекции, стоимость восстановления, возможна потеря работы.
Короткие рекомендации:
- Если можно задать глобальный порядок — используйте его (самый простой и быстрый).
- Для динамики — trylock/timeout с бэкоффом.
- В системах с критичной надежностью — детекция и восстановление (или транзакции с откатом).
- Комбинируйте подходы: порядок для большинства случаев + таймаут/trylock для редких динамических ситуаций.