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

Что такое DeadLock?

1.0 Junior🔥 201 комментариев
#Асинхронность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

DeadLock (Мёртвая блокировка) в многопоточности

Deadlock — это состояние, когда два или более потока бесконечно ждут друг друга, что приводит к полной остановке приложения. Это одна из самых сложных проблем в многопоточном программировании.

Классический пример

Представь, что у тебя есть два человека и две палочки для еды (chopsticks). Каждый человек должен взять обе палочки, чтобы поесть.

Поток 1: берет палочку A → ждет палочку B
Поток 2: берет палочку B → ждет палочку A

Каждый поток занял один ресурс и ждет другой. Они ждут друг друга бесконечно — это DeadLock!

Четыре условия DeadLock (все должны быть выполнены)

1. Mutual Exclusion (Взаимное исключение) Ресурс не может быть использован двумя потоками одновременно:

final mutex = Mutex();
await mutex.lock(); // Только один поток может владеть блокировкой

2. Hold and Wait (Держание и ожидание) Поток держит ресурс и ждет другого:

await lock1.lock();
// ... вот тут может наступить deadlock
await lock2.lock();

3. No Preemption (Невозможность отобрания) Ресурс нельзя отобрать, пока поток его держит:

// Нельзя просто так взять ресурс у другого потока
// Нужно дождаться, пока он его освободит

4. Circular Wait (Циклическое ожидание) Цепь потоков, где каждый ждет ресурс от следующего:

Поток A → ждет Поток B
Поток B → ждет Поток C
Поток C → ждет Поток A (цикл!)

Практический пример DeadLock в Flutter/Dart

class BankAccount {
  int balance = 100;
  final Completer<void> _lockCompleter = Completer();

  Future<void> transfer(int amount, BankAccount other) async {
    // Поток 1: блокирует это счете
    await _lockCompleter.future; // Имитация lock
    
    // Пытается заблокировать другой счет
    await other._lockCompleter.future; // DEADLOCK!
    
    balance -= amount;
    other.balance += amount;
  }
}

// Сценарий deadlock:
// Поток 1: account1.transfer(10, account2) → берет account1, ждет account2
// Поток 2: account2.transfer(20, account1) → берет account2, ждет account1
// Результат: оба потока ждут друг друга бесконечно

Реальный пример в многопоточности

class Resource {
  final String name;
  Resource(this.name);
}

final resourceA = Resource('A');
final resourceB = Resource('B');

// ❌ DEADLOCK SCENARIO
Future<void> deadlockExample() async {
  Future<void> thread1() async {
    synchronized(resourceA) { // Берет ResourceA
      await Future.delayed(Duration(milliseconds: 100));
      synchronized(resourceB) { // Ждет ResourceB
        print('Thread 1 has both resources');
      }
    }
  }

  Future<void> thread2() async {
    synchronized(resourceB) { // Берет ResourceB
      await Future.delayed(Duration(milliseconds: 100));
      synchronized(resourceA) { // Ждет ResourceA
        print('Thread 2 has both resources');
      }
    }
  }

  // Запускаем оба потока одновременно
  await Future.wait([thread1(), thread2()]);
  // ⚠️ Программа зависнет!
}

Как избежать DeadLock

1. Всегда блокируй ресурсы в одном порядке

// ✅ ПРАВИЛЬНО: всегда сначала A, потом B
Future<void> safeTransfer() async {
  // Поток 1
  synchronized(resourceA) {
    synchronized(resourceB) {
      // Операция
    }
  }
}

Future<void> safeTransfer2() async {
  // Поток 2 тоже в том же порядке
  synchronized(resourceA) {
    synchronized(resourceB) {
      // Операция
    }
  }
}

2. Установи timeout

Future<void> transferWithTimeout() async {
  try {
    await lock1.lock();
    
    final result = await lock2.lock().timeout(
      Duration(seconds: 5),
      onTimeout: () => throw TimeoutException('DeadLock detected'),
    );
    
    // Операция
  } catch (e) {
    print('Lock timeout or error: $e');
  } finally {
    lock1.unlock();
    lock2.unlock();
  }
}

3. Используй atomic операции и CAS (Compare-And-Swap)

// Вместо множественных блокировок, используй одну атомарную операцию
class AtomicAccount {
  int _balance = 0;
  final Mutex _lock = Mutex();

  Future<bool> transferAtomic(int amount) async {
    return await _lock.protect(() async {
      if (_balance >= amount) {
        _balance -= amount;
        return true;
      }
      return false;
    });
  }
}

4. Используй higher-level abstractions

// Вместо низкоуровневых lock/unlock, используй:
import 'package:synchronized/synchronized.dart';

final lock = Lock();

future<void> safeOperation() async {
  await lock.synchronized(() async {
    // Код выполняется с гарантией синхронизации
    // Нет риска deadlock благодаря высокоуровневому API
  });
}

Детектирование DeadLock

class DeadlockDetector {
  static Future<T> executeWithTimeout<T>(
    Future<T> Function() operation,
    Duration timeout,
  ) async {
    try {
      return await operation().timeout(timeout);
    } on TimeoutException {
      print('⚠️ Possible deadlock detected!');
      rethrow;
    }
  }
}

// Использование
await DeadlockDetector.executeWithTimeout(
  () => complexOperation(),
  Duration(seconds: 10),
);

DeadLock в контексте Flutter

UI Thread DeadLock

// ❌ ПЛОХО: блокируешь UI thread
void onButtonPressed() {
  // Длинная синхронная операция → UI зависает
  Thread.sleep(5000);
  // Даже если другой код ждет UI, ничего не происходит
}

// ✅ ХОРОШО: используй async/await
void onButtonPressed() async {
  await longOperation(); // UI остается отзывчивым
}

Практические правила

  1. Минимизируй время, когда поток держит lock
  2. Всегда освобождай lock (используй try-finally или synchronized)
  3. Избегай вложенных locks где возможно
  4. Документируй порядок, в котором должны приниматься locks
  5. Используй tools для детектирования (unit tests, static analysis)
  6. Тестируй многопоточные сценарии (race conditions, stress tests)

Заключение

Deadlock — это призрак многопоточности. За 10+ лет опыта я вижу, что это одна из самых сложных багов для отладки, потому что она непредсказуема и сложно воспроизводится. Ключ к избежанию:

  • Простота — используй высокоуровневые abstractions
  • Осторожность — знай все четыре условия deadlock
  • Тестирование — stress-тесты и сценарии race conditions
Что такое DeadLock? | PrepBro