Skip to content

misterserge/race-locks-solutions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Проблемы параллелизма и их решения на Node.js

Практические примеры основных проблем параллельного программирования с рабочими решениями.

Запуск примеров

# Установка (если нужно)
npm install

# Запуск отдельных примеров
npm run racing      # Race Condition
npm run deadlock    # Deadlock
npm run starvation  # Starvation
npm run livelock    # Livelock

# Или напрямую
node 1-racing.js
node 2-deadlock.js
node 3-starvation.js
node 4-livelock.js

1. Race Condition (Гонка данных) 🏁

Проблема

Несколько асинхронных операций одновременно читают и модифицируют общие данные без синхронизации.

Пример: Два одновременных снятия по 600₽ с баланса 1000₽ → баланс становится -200₽ вместо 400₽.

Решения

✅ Mutex (Mutual Exclusion)

Гарантирует, что только один поток/задача имеет доступ к критической секции.

class Mutex {
    async lock() { /* ждем, пока не освободится */ }
    unlock() { /* освобождаем */ }
}

✅ Atomic Operations (SharedArrayBuffer + Atomics)

Для простых операций (инкремент, сравнение) используем атомарные инструкции CPU.

Atomics.compareExchange(array, index, expected, newValue);

Когда использовать:

  • Mutex: сложная бизнес-логика
  • Atomics: простые операции с числами

2. Deadlock (Взаимная блокировка) 🔒

Проблема

Две задачи ждут ресурсы друг друга:

  • Task-1 захватил A, ждет B
  • Task-2 захватил B, ждет A
  • Обе зависли навсегда

Решения

✅ Resource Ordering (Упорядочение)

ВСЕ задачи захватывают ресурсы в одинаковом порядке (например, всегда A → B).

// Все всегда делают: lock(A) -> lock(B)
// НИКОГДА: lock(B) -> lock(A)

✅ Timeout + Retry

Если не удалось захватить ресурс за N мс → откатываемся и пробуем снова.

const success = await mutex.lock(500); // timeout 500ms
if (!success) {
    // откат и повтор
}

✅ Try-Lock (Неблокирующий захват)

Пытаемся захватить без блокировки. Если занят → откатываемся.

if (!mutex.tryLock()) {
    // не смогли захватить, откатываемся
}

Когда использовать:

  • Ordering: когда можно контролировать порядок
  • Timeout: когда важна отказоустойчивость
  • Try-lock: для высоконагруженных систем

3. Starvation (Голодание) 🍽️

Проблема

Высокоприоритетные задачи постоянно захватывают ресурс, низкоприоритетные никогда не получают доступ.

Пример: 3 быстрые задачи делают 60 операций, 1 медленная — 0 операций за то же время.

Решения

✅ Fair Mutex (FIFO очередь)

Ресурс выдается строго по порядку прихода запросов.

class FairMutex {
    queue = []; // FIFO
    async lock() {
        await queue.push(promise);
    }
}

✅ Priority Aging (Старение приоритетов)

Чем дольше задача ждет, тем выше ее эффективный приоритет.

effectivePriority = basePriority + waitTime;

✅ Rate Limiting

Ограничиваем количество операций на задачу в единицу времени.

// Макс 5 операций в секунду на задачу
await rateLimitedMutex.lock(taskName, 5);

Когда использовать:

  • Fair FIFO: когда все задачи равноправны
  • Aging: когда есть приоритеты, но нужно избежать голодания
  • Rate limiting: когда нужно ограничить агрессивных потребителей

4. Livelock (Живая блокировка) 🔄

Проблема

Задачи активно работают, но не прогрессируют — постоянно отменяют свои действия.

Пример:

  • Task-1: захватил A, не может взять B → освобождает A
  • Task-2: захватил B, не может взять A → освобождает B
  • Повторяется бесконечно

Решения

✅ Random Backoff

Случайные задержки разрывают синхронизацию конфликтующих задач.

const backoff = Math.random() * 150;
await sleep(backoff);

✅ Coordinator (Координатор)

Центральный арбитр атомарно выдает ВСЕ необходимые ресурсы сразу.

const resources = await coordinator.requestResources();
// получили A и B атомарно

✅ Lock Ordering + Timeout

Комбинация упорядочения и timeout'ов предотвращает зацикливание.

✅ Actor Model / Message Passing

Акторы общаются через асинхронные сообщения, избегая блокировок.

Когда использовать:

  • Random backoff: простое и эффективное решение для большинства случаев
  • Coordinator: когда нужны гарантии прогресса
  • Lock ordering: когда можно контролировать порядок
  • Actor model: для распределенных систем

Сравнительная таблица

Проблема Суть Прогресс системы Лучшее решение
Race Condition Конкурентная модификация данных Есть, но данные некорректны Mutex / Atomics
Deadlock Циклическое ожидание ресурсов Нет (полная остановка) Resource Ordering
Starvation Задачи не получают ресурсы Есть, но несправедливый Fair FIFO / Priority Aging
Livelock Постоянная отмена действий Видимый, но бесполезный Random Backoff / Coordinator

Общие принципы

Правила предотвращения проблем:

  1. Минимизируйте критические секции — чем меньше код под блокировкой, тем меньше конфликтов
  2. Избегайте вложенных блокировок — если нельзя избежать, используйте строгий порядок
  3. Используйте таймауты — бесконечное ожидание = потенциальная проблема
  4. Lock-free структуры данных — если возможно, используйте атомарные операции
  5. Тестируйте под нагрузкой — проблемы параллелизма проявляются при высокой конкуренции

Выбор решения:

Простые операции с числами? 
    → Atomics

Сложная бизнес-логика?
    → Mutex

Несколько ресурсов?
    → Resource Ordering + Timeout

Есть приоритеты?
    → Priority Aging

Распределенная система?
    → Actor Model / Message Passing

Требования

  • Node.js >= 14.0 (для SharedArrayBuffer)
  • Нет внешних зависимостей

Литература

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published