Конкурентность против параллелизма
Урок разводит два часто путаемых понятия и объясняет, почему этот курс — про параллелизм.
Конкурентность — про структуру: как организовать программу из многих задач, которые продвигаются «вперемешку». Параллелизм — про исполнение: как заставить вычисления идти физически одновременно ради скорости.
Метафора повара
Один повар, который, ожидая закипания воды, режет овощи, а потом возвращается к плите — это конкурентность: он жонглирует несколькими задачами, но физически делает что-то одно в каждый момент. Два повара на одной кухне, готовящие два блюда одновременно — это параллелизм: в одну единицу времени реально выполняются две работы. Конкурентность возможна и на одном ядре (переключение между задачами), параллелизм требует нескольких вычислителей.
Зачем различать
Курс «Конкурентность и многопоточность» на этом сайте — про другое: про синхронизацию, гонки данных, блокировки, корректное разделение состояния между потоками. Там главный враг — ошибка корректности (две задачи испортили общую переменную). Здесь, в курсе про параллельные алгоритмы, главная цель — скорость: как разложить вычисление так, чтобы N устройств сократили время. Эти темы пересекаются (параллельный алгоритм часто использует потоки и должен избегать гонок), но угол зрения разный.
| Аспект | Конкурентность | Параллелизм |
| Главный вопрос | Как структурировать много задач? | Как ускорить одну задачу? |
| Нужно ли >1 ядра | Нет | Да |
| Главный риск | Гонки, дедлоки | Малое ускорение, дисбаланс |
| Типичный пример | Веб-сервер на 1000 соединений | Сложить миллион чисел на 8 ядрах |
Когда они встречаются вместе
Веб-сервер, обрабатывающий тысячи соединений на восьмиядерном процессоре, и конкурентен (много задач в полёте), и параллелен (восемь ядер реально работают сразу). Но это два независимых свойства. Можно быть конкурентным без параллелизма (один поток с асинхронным циклом событий) и параллельным без сложной конкурентности (восемь ядер тупо суммируют свои куски, не разделяя состояние).
Различие не педантичное, а практическое: оно определяет, какой инструмент брать. Если ваша задача — обслуживать много медленных операций ввода-вывода (запросы к базе, сетевые вызовы), вам нужна конкурентность: пока одна операция ждёт ответа, поток занимается другой. Добавление ядер тут почти не поможет, потому что ядра всё равно простаивали бы в ожидании. Если же задача — перемолоть большой массив чисел, вам нужен параллелизм: разложить счёт на ядра. Конкурентность здесь не нужна вовсе, ведь нет ожидания, которое надо чем-то заполнять. Спутав эти случаи, легко потратить силы зря: накрутить асинхронность там, где надо было распараллелить счёт, или наоборот.
Как работает под капотом
Конкурентность реализуется планировщиком, который чередует задачи: дал поработать одной, сохранил её состояние, переключился на другую. На одном ядре это создаёт иллюзию одновременности. Параллелизм же опирается на аппаратуру: разные ядра имеют собственные регистры и АЛУ и действительно выполняют инструкции в один такт. Наша последовательная эмуляция в браузере показывает логику параллельного алгоритма, но реальное ускорение даёт только железо.
Маленькая модель «два повара»
tasks = ["суп", "салат", "десерт", "напиток"]
# конкурентно на 1 ядре: задачи идут по очереди, но "переключаемся"
print("=== 1 повар (конкурентно) ===")
for t in tasks:
print("готовлю", t)
# параллельно на 2 поваров: раздаём по ролям (здесь печатаем раскладку)
print("=== 2 повара (параллельно) ===")
for i, t in enumerate(tasks):
print("повар", i % 2 + 1, "->", t)
Вывод:
=== 1 повар (конкурентно) === готовлю суп готовлю салат готовлю десерт готовлю напиток === 2 повара (параллельно) === повар 1 -> суп повар 2 -> салат повар 1 -> десерт повар 2 -> напиток
Частые ошибки
- Считать, что асинхронный код (один поток, event loop) «параллелен». Он конкурентен, но на одном ядре не ускоряет счёт.
- Думать, что добавление потоков всегда даёт параллелизм. Если ядро одно, потоки лишь чередуются.
- Переносить все приёмы из курса конкурентности сюда. Гонки важны, но цель здесь — ускорение, а не только корректность.
Итоги
- Конкурентность — про структуру многих задач, параллелизм — про одновременное исполнение ради скорости.
- Параллелизм требует нескольких вычислителей; конкурентность — нет.
- Этот курс делает акцент на скорости: как спроектировать алгоритм под много ядер/GPU.