> В чем разница между мультитредингом и мультипроцессингом в Python (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: Sunlight

Стек: Python

> Пример ответа

Короткий ответ

Мультитрединг - это конкурентное выполнение потоков в рамках одного процесса, ограниченное GIL для CPU-bound задач. Мультипроцессинг - это параллельное выполнение отдельных процессов, каждый со своим интерпретатором и памятью, что обходит GIL. Для I/O-bound задач эффективен мультитрединг, для CPU-bound - мультипроцессинг. Выбор зависит от типа нагрузки и накладных расходов на создание и синхронизацию.

Подробное объяснение

Основное различие лежит в модели выполнения и взаимодействия с GIL (Global Interpreter Lock). В CPython GIL позволяет выполнять только один поток нативного кода Python за раз. Это значит, что потоки не дают реального параллелизма для CPU-bound операций - они лишь переключаются между собой, создавая иллюзию параллельности. Для I/O-bound задач, где поток большую часть времени ожидает ввода-вывода, GIL освобождается, и переключение между потоками даёт значительный выигрыш в пропускной способности.

Мультипроцессинг создаёт отдельные процессы, каждый со своим собственным интерпретатором Python и отдельной памятью. GIL существует в каждом процессе отдельно, поэтому CPU-bound задачи выполняются по-настоящему параллельно на многоядерных системах. Однако это требует межпроцессного взаимодействия (IPC) через pipes, queues или shared memory, что дороже, чем общая память потоков.

Ключевые trade-off:

  • Потоки: лёгкие, быстрый старт, общая память, но ограничены GIL для CPU-bound и требуют осторожности с гонками данных.
  • Процессы: тяжёлые, медленный старт, изолированная память, но полный параллелизм и лучшая устойчивость к сбоям (падение процесса не убивает родителя).

Также важно учитывать, что в Python 3.13 появился экспериментальный режим без GIL (free-threaded build), но он пока не является стандартом для production.

На практике

Для backend-разработки выбор обычно диктуется характером нагрузки:

  • Если сервис делает много сетевых запросов, работы с БД, чтения файлов - это I/O-bound, и мультитрединг через threading или asyncio будет оптимален.
  • Если сервис выполняет тяжёлые вычисления, обработку изображений, парсинг больших данных - это CPU-bound, и нужен multiprocessing или concurrent.futures.ProcessPoolExecutor.

На практике часто используют гибридный подход: процессы для CPU-bound воркеров, потоки для I/O-bound операций внутри каждого процесса. Например, веб-сервер на uvicorn с несколькими worker-процессами, каждый из которых использует event loop для асинхронной обработки запросов.

Стоит помнить о накладных расходах: создание процесса дороже создания потока, а передача данных между процессами требует сериализации (pickle), что добавляет latency. Для больших объёмов данных между процессами лучше использовать shared memory или multiprocessing.Manager.

Пример кода

PYTHON
# CPU-bound: мультипроцессинг даёт реальный параллелизм
from multiprocessing import Pool
def cpu_heavy(n):
return sum(i * i for i in range(n))
with Pool(4) as pool:
results = pool.map(cpu_heavy, [10_000_000] * 4)
PYTHON
# I/O-bound: мультитрединг эффективен, GIL не мешает
import threading
import time
import requests
def fetch(url):
return requests.get(url).status_code
urls = ["https://example.com"] * 10
threads = [threading.Thread(target=fetch, args=(u,)) for u in urls]
for t in threads:
t.start()
for t in threads:
t.join()

Как отвечать на собеседовании

Начните с чёткого определения обоих подходов, затем сразу укажите на GIL как ключевой фактор. Приведите конкретный пример, когда один подход лучше другого. Покажите понимание trade-off: стоимость создания, синхронизация, передача данных. Упомяните, что для I/O-bound в современном Python чаще используют asyncio, а не потоки, но это отдельная тема. Если спросят про Python 3.13 free-threading - упомяните, что это экспериментально и не меняет текущую практику.

Что проверяет интервьюер

Интервьюер проверяет:

  • Понимание GIL и его влияния на потоки.
  • Умение различать I/O-bound и CPU-bound задачи.
  • Знание накладных расходов и ограничений обоих подходов.
  • Практический опыт: когда и что выбирать в реальном backend.
  • Глубину: знание IPC, shared memory, concurrent.futures.

Типичные ошибки

  • Утверждение, что мультитрединг в Python бесполезен вообще - это неверно, он отлично работает для I/O.
  • Игнорирование GIL при обсуждении мультипроцессинга - именно для обхода GIL он и нужен.
  • Путаница между threading и asyncio - это разные модели, хотя обе подходят для I/O-bound.
  • Забывают упомянуть, что multiprocessing требует сериализации данных, что может стать узким местом.
  • Предложение использовать мультипроцессинг для каждой задачи - это overengineering, если задача I/O-bound.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью