Конкурентность 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-bound | CPU-bound |
|---|---|---|
| Узкое место | Ожидание внешних ресурсов (сеть, диск) | Вычисления на процессоре |
| Примеры | HTTP-запросы, чтение файлов, SQL-запросы | Парсинг JSON (большого), криптография, ML inference |
| Event loop | Справляется отлично | Блокируется |
| Решение | async/await, конкурентность | Параллелизм (Workers) |
Модели конкурентности в JavaScript
JavaScript использует модель событий (event-driven) — вся конкурентность через event loop и очереди. Это отличается от моделей в других языках:
| Модель | Язык | Суть |
|---|---|---|
| Event loop | JavaScript | Один поток + очереди задач |
| Goroutines (CSP) | Go | Легковесные потоки + каналы |
| Actor model | Erlang, Akka | Акторы обмениваются сообщениями |
| Threads + locks | Java, C++ | Потоки ОС + мьютексы |
| Workers | JavaScript | Отдельные потоки + postMessage |
Web Workers ближе всего к Actor model: изолированные потоки обмениваются сообщениями. Нет общего состояния (по умолчанию), нет блокировок, нет гонок данных.
Итого
| Факт | Описание |
|---|---|
| Конкурентность | Переключение между задачами в одном потоке (event loop) |
| Параллелизм | Одновременное выполнение в нескольких потоках (Workers) |
| I/O-bound | Event loop справляется — async/await достаточно |
| CPU-bound | Нужен параллелизм — Workers, worker_threads |
| JS-модель | Event-driven, близка к Actor model при использовании Workers |