Learning Book

Конкурентность vs параллелизм

Определения

Конкурентность (concurrency) — способность системы обрабатывать несколько задач, переключаясь между ними. Задачи перекрываются во времени, но не обязательно выполняются одновременно.

Параллелизм (parallelism) — выполнение нескольких задач буквально одновременно на разных вычислительных единицах (ядрах, процессорах).

Конкурентность:  [A---]  [B--]  [A--]  [B---]   ← один поток, переключение
Параллелизм:     [A-----------]                  ← два потока, одновременно
                 [B-----------]

Конкурентность — про структуру программы. Параллелизм — про выполнение.

Аналогия

Один повар, два блюда (конкурентность): повар режет овощи для салата, ставит кастрюлю на плиту, пока вода закипает — возвращается к салату. Одна пара рук, но два блюда продвигаются.

Два повара, два блюда (параллелизм): каждый готовит своё. Буквально одновременно.

JavaScript c event loop — один повар. Web Workers — возможность нанять второго.

Модель JavaScript

JavaScript однопоточный. Event loop даёт конкурентность: пока ждём ответ от сервера, обрабатываем клик. Но весь код выполняется в одном потоке — настоящего параллелизма нет.

// Конкурентность: event loop переключается между задачами
fetch('/api/users')        // запрос уходит — поток свободен
  .then(r => r.json())
  .then(users => render(users))

handleClick()              // обрабатывается пока ждём fetch

Для I/O-задач (сеть, файлы, БД) этого достаточно — поток не занят вычислениями, он просто ждёт. Но для CPU-bound задач event loop не поможет.

Когда event loop недостаточно

Event loop отлично справляется с I/O — потому что ожидание делегируется ОС. Но CPU-bound код блокирует единственный поток:

// ❌ Блокирует event loop на секунды
function fibonacci(n) {
  if (n <= 1) return n
  return fibonacci(n - 1) + fibonacci(n - 2)
}

fibonacci(45) // ~7 секунд — UI заморожен, события не обрабатываются

Пока fibonacci(45) считается, JavaScript не может:

  • Обрабатывать клики и скроллы
  • Обновлять анимации
  • Отвечать на запросы (в Node.js)

Решение — вынести вычисления в отдельный поток. Для этого существуют Web Workers (браузер) и worker_threads (Node.js).

CPU-bound vs I/O-bound

ХарактеристикаI/O-boundCPU-bound
Узкое местоОжидание внешних ресурсов (сеть, диск)Вычисления на процессоре
ПримерыHTTP-запросы, чтение файлов, SQL-запросыПарсинг JSON (большого), криптография, ML inference
Event loopСправляется отличноБлокируется
Решениеasync/await, конкурентностьПараллелизм (Workers)

Модели конкурентности в JavaScript

JavaScript использует модель событий (event-driven) — вся конкурентность через event loop и очереди. Это отличается от моделей в других языках:

МодельЯзыкСуть
Event loopJavaScriptОдин поток + очереди задач
Goroutines (CSP)GoЛегковесные потоки + каналы
Actor modelErlang, AkkaАкторы обмениваются сообщениями
Threads + locksJava, C++Потоки ОС + мьютексы
WorkersJavaScriptОтдельные потоки + postMessage

Web Workers ближе всего к Actor model: изолированные потоки обмениваются сообщениями. Нет общего состояния (по умолчанию), нет блокировок, нет гонок данных.

Итого

ФактОписание
КонкурентностьПереключение между задачами в одном потоке (event loop)
ПараллелизмОдновременное выполнение в нескольких потоках (Workers)
I/O-boundEvent loop справляется — async/await достаточно
CPU-boundНужен параллелизм — Workers, worker_threads
JS-модельEvent-driven, близка к Actor model при использовании Workers