← Назад к вопросам
Когда возникает Deadlock?
2.0 Middle🔥 241 комментариев
#Архитектура и паттерны#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Deadlock в системах
Deadlock (взаимоблокировка) — это ситуация, когда два или более процесса (потока) бесконечно ждут друг друга, находясь в состоянии блокировки. Результат: система зависает, процессы никогда не завершатся.
Когда возникает Deadlock
1. Классический пример: два потока с двумя ресурсами
import threading
import time
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread1_func():
with lock1:
print("Thread 1 захватил lock1")
time.sleep(0.1)
with lock2:
print("Thread 1 захватил lock2")
def thread2_func():
with lock2:
print("Thread 2 захватил lock2")
time.sleep(0.1)
with lock1:
print("Thread 2 захватил lock1")
t1 = threading.Thread(target=thread1_func)
t2 = threading.Thread(target=thread2_func)
t1.start()
t2.start()
t1.join() # Зависнет здесь!
t2.join()
Что произойдёт:
- Thread 1 захватывает lock1, ждёт lock2
- Thread 2 захватывает lock2, ждёт lock1
- Deadlock! Оба потока ждут друг друга бесконечно
2. Deadlock в базах данных
# Транзакция 1
connection1.execute("UPDATE users SET balance = balance - 100 WHERE id = 1")
connection1.execute("UPDATE users SET balance = balance + 100 WHERE id = 2")
# Транзакция 2 (одновременно)
connection2.execute("UPDATE users SET balance = balance - 100 WHERE id = 2")
connection2.execute("UPDATE users SET balance = balance + 100 WHERE id = 1")
Деадлок возникает, когда:
- Транзакция 1 заблокировала строку id=1, ждёт id=2
- Транзакция 2 заблокировала строку id=2, ждёт id=1
3. Deadlock с asyncio
import asyncio
lock1 = asyncio.Lock()
lock2 = asyncio.Lock()
async def coro1():
async with lock1:
print("Coroutine 1 захватила lock1")
await asyncio.sleep(0.1)
async with lock2:
print("Coroutine 1 захватила lock2")
async def coro2():
async with lock2:
print("Coroutine 2 захватила lock2")
await asyncio.sleep(0.1)
async with lock1:
print("Coroutine 2 захватила lock1")
async def main():
await asyncio.gather(coro1(), coro2()) # Deadlock!
asyncio.run(main())
Необходимые условия (Условия Coffman)
Для возникновения deadlock'а нужны ВСЕ четыре условия одновременно:
1. Взаимное исключение (Mutual Exclusion)
Ресурс может использоваться только одним процессом.
2. Удержание и ожидание (Hold and Wait)
Процесс держит один ресурс и ждёт другого.
3. Отсутствие вытеснения (No Preemption)
Отнять ресурс у процесса нельзя. Только сам отпустит.
4. Циклическое ожидание (Circular Wait)
Образуется цепочка: P1 ждёт P2 → P2 ждёт P3 → Pn ждёт P1
Как избежать Deadlock
1. Упорядочение ресурсов (Lock Ordering)
# ❌ Опасно
def transfer1():
with lock_account1:
with lock_account2:
pass
def transfer2():
with lock_account2: # Обратный порядок!
with lock_account1:
pass
# ✅ Безопасно - одинаковый порядок везде
def transfer1():
with lock_account1:
with lock_account2:
pass
def transfer2():
with lock_account1:
with lock_account2:
pass
2. Timeout при захвате
acquired = lock.acquire(timeout=1.0)
if not acquired:
print("Timeout - выходим")
return
3. Использование очередей
import queue
# Thread-safe queue не требует manual locking
q = queue.Queue()
4. RLock (Reentrant Lock)
import threading
lock = threading.RLock()
Deadlock'и — критическая проблема в многопоточных и асинхронных системах. Понимание их причин и методов предотвращения — необходимо для надёжного software engineering.