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

> Конкурентность — структура программы из нескольких одновременно идущих задач; параллелизм — их физически одновременное выполнение на нескольких ядрах. Разные понятия с разными инструментами.

Источник: https://llmdocs.ru/wiki/concurrency-parallelism/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Конкурентность**: работа с несколькими задачами, чередующимися во времени
- **Параллелизм**: одновременное выполнение на нескольких ядрах или машинах
- **Механизмы**: процессы, потоки, корутины (async/await), зелёные потоки, акторы
- **Задачи с ожиданием ввода-вывода**: асинхронность, потоки
- **Вычислительные задачи**: процессы, потоки без глобальной блокировки, GPU
- **Ограничение ускорения**: закон Амдала (1967)
- **Проблемы**: гонки данных, взаимоблокировки, голодание
**Конкурентность** (англ. *concurrency*) — свойство программы состоять из нескольких независимых задач, выполнение которых перекрывается во времени: пока одна ждёт ответа сети, другая обрабатывает данные. **Параллелизм** (*parallelism*) — физически одновременное выполнение вычислений на нескольких ядрах процессора, нескольких машинах или GPU. Роб Пайк сформулировал разницу так: конкурентность — это *работа* со многими вещами сразу, параллелизм — *выполнение* многих вещей сразу.[1]

Конкурентная программа может работать на одном ядре, чередуя задачи; параллельная обязательно использует несколько вычислителей. Повар, который ставит воду и, пока она закипает, режет овощи, работает конкурентно; два повара на одной кухне — параллельно.

## Зачем это нужно

С середины 2000-х тактовая частота процессоров почти перестала расти, а количество ядер — нет. Герб Саттер назвал это концом «бесплатного обеда»: программы перестали ускоряться сами собой с каждым новым поколением процессоров, и для использования всех ядер их приходится писать параллельными.[2] Одновременно серверные приложения обслуживают тысячи соединений, большую часть времени ожидающих сети или диска, — это задача конкурентности.

## Два класса задач

| | Ограничены вводом-выводом (*I/O-bound*) | Ограничены процессором (*CPU-bound*) |
|---|---|---|
| Пример | веб-сервер, парсер сайтов, вызовы API, запросы к БД | обработка изображений, численные расчёты, сжатие, обучение моделей |
| Узкое место | ожидание сети и диска | вычисления |
| Что помогает | конкурентность: async/await, потоки | параллелизм: процессы, потоки на разных ядрах, векторизация, GPU |

## Механизмы

- **Процессы** — изолированная память, связь через каналы, сокеты или общую память. Надёжно, но дороже по ресурсам.
- **Потоки** (*threads*) — общая память внутри процесса; управляет ими операционная система. Дешевле процессов, но общий доступ к данным требует синхронизации.
- **Корутины и async/await** — задачи сами уступают управление в точках ожидания, а цикл событий переключается между ними в одном потоке. Тысячи одновременных соединений почти без накладных расходов — так работают [asyncio](https://llmdocs.ru/wiki/asyncio.md) в Python, JavaScript и асинхронный Rust.
- **Зелёные потоки** — лёгкие потоки, которыми управляет среда выполнения языка: [горутины](https://llmdocs.ru/wiki/goroutines-channels.md) в [Go](https://llmdocs.ru/wiki/go.md), виртуальные потоки в Java. Совмещают простоту обычного кода с масштабом асинхронности.
- **Акторы и передача сообщений** — задачи не делят память, а обмениваются сообщениями (Erlang, Akka). Принцип Go: «не общайтесь через общую память — делитесь памятью через общение».[6]

## Пример в Python

```python
import asyncio, httpx
from concurrent.futures import ProcessPoolExecutor

# I/O-bound: 100 запросов конкурентно в одном потоке
async def fetch_all(urls):
    async with httpx.AsyncClient() as client:
        return await asyncio.gather(*(client.get(u) for u in urls))

# CPU-bound: вычисления параллельно на всех ядрах
def heavy(n: int) -> int:
    return sum(i * i for i in range(n))

if __name__ == "__main__":
    with ProcessPoolExecutor() as pool:
        results = list(pool.map(heavy, [10**7] * 8))
```

В CPython глобальная блокировка интерпретатора (GIL) не даёт потокам выполнять Python-код параллельно, поэтому для вычислений используют процессы.[3] PEP 703 сделал GIL необязательным: начиная с Python 3.13 существует экспериментальная сборка без GIL, а в 3.14 она получила официальную поддержку (см. [GIL и free-threading (PEP 703)](https://llmdocs.ru/wiki/gil-free-threading.md)).[4]

## Проблемы

- **Состояние гонки.** Результат зависит от того, в каком порядке потоки обратились к общим данным: два потока одновременно читают счётчик, прибавляют единицу и записывают — одно увеличение теряется (см. [Состояние гонки и блокировки](https://llmdocs.ru/wiki/race-conditions-locks.md)).
- **Взаимоблокировка** (*deadlock*). Два потока ждут блокировки, захваченные друг другом, и стоят вечно.
- **Голодание и инверсия приоритетов.** Задача бесконечно не получает ресурс.
- **Недетерминированность.** Ошибки проявляются редко и не воспроизводятся в отладчике.

Средства защиты — мьютексы, атомарные операции, неизменяемые данные, очереди и каналы вместо общей памяти. [Rust](https://llmdocs.ru/wiki/rust.md) проверяет отсутствие гонок данных на этапе компиляции с помощью системы владения и типажей `Send` и `Sync`.[5]

## Закон Амдала

Ускорение от параллелизма ограничено долей программы, которая выполняется последовательно. Если 10% работы нельзя распараллелить, то даже на бесконечном числе ядер программа ускорится не больше чем в 10 раз. Отсюда практический вывод: прежде чем добавлять ядра, найдите и сократите последовательную часть — блокировки, общий ресурс, однопоточный этап.

## Параллелизм за пределами одной машины

На уровне систем те же идеи масштабируются: очереди задач и пулы обработчиков, горизонтальное масштабирование сервисов в [Kubernetes](https://llmdocs.ru/wiki/kubernetes.md), распределённые вычисления. Там к гонкам добавляются сетевые задержки и частичные отказы — это предмет [распределённых систем](https://llmdocs.ru/wiki/distributed-systems.md).

## См. также

- [Состояние гонки и блокировки](https://llmdocs.ru/wiki/race-conditions-locks.md)
- [asyncio](https://llmdocs.ru/wiki/asyncio.md)
- [Горутины и каналы](https://llmdocs.ru/wiki/goroutines-channels.md)
- [GIL и free-threading (PEP 703)](https://llmdocs.ru/wiki/gil-free-threading.md)
- [Распределённые системы](https://llmdocs.ru/wiki/distributed-systems.md)

## Источники

1. [Rob Pike — Concurrency is not Parallelism (2012)](https://go.dev/blog/waza-talk)
2. [Herb Sutter — The Free Lunch Is Over (2005)](http://www.gotw.ca/publications/concurrency-ddj.htm)
3. [Python — concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html)
4. [PEP 703 — Making the Global Interpreter Lock Optional in CPython](https://peps.python.org/pep-0703/)
5. [The Rust Programming Language — Fearless Concurrency](https://doc.rust-lang.org/book/ch16-00-concurrency.html)
6. [Go — Effective Go: Concurrency](https://go.dev/doc/effective_go#concurrency)
