← Назад к вопросам

Когда возникает 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.

Когда возникает Deadlock? | PrepBro