В чём разница между многопоточностью и многопроцессорностью в Python?

«В чём разница между многопоточностью и многопроцессорностью в Python?» — вопрос из категории Python, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Python эти концепции реализованы с учётом Global Interpreter Lock (GIL), что накладывает ключевые ограничения.

Многопоточность (threading)

  • Что это: Запуск нескольких потоков выполнения в рамках одного процесса. Потоки разделяют общую память (глобальные переменные, структуры данных).
  • В Python (из-за GIL): GIL позволяет выполняться только одному потоку Python в любой момент времени, даже на многоядерном CPU. Это делает чистые потоки Python неэффективными для CPU-задач (вычисления, обработка изображений).
  • Для чего использовать: Для I/O-bound задач, где потоки большую часть времени ожидают (сеть, диск, ввод-вывод). Пока один поток ждёт, GIL может быть передан другому.
    
    import threading
    import requests

def download_url(url): response = requests.get(url) print(f"Downloaded {len(response.content)} bytes from {url}")

urls = ["https://example.com/1", "https://example.com/2"] threads = [] for url in urls: thread = threading.Thread(target=download_url, args=(url,)) thread.start() threads.append(thread) for t in threads: t.join() # I/O-bound задача хорошо распараллеливается потоками


### Многопроцессорность (`multiprocessing`)
*   **Что это:** Запуск нескольких **независимых процессов**, каждый со своим интерпретатором Python и памятью. Обмен данными требует специальных механизмов (очереди, каналы, shared memory).
*   **В Python (обход GIL):** Каждый процесс имеет свой GIL и может выполняться на отдельном ядре CPU. Это делает многопроцессорность **эффективной для CPU-задач**.
*   **Для чего использовать:** Для **CPU-bound задач** (математические расчёты, обработка данных).
```python
import multiprocessing
import math

def calculate_factorial(n):
    print(f"Calculating {n}!")
    return math.factorial(n)

if __name__ == '__main__':
    numbers = [1000, 2000, 3000]
    with multiprocessing.Pool(processes=3) as pool:
        results = pool.map(calculate_factorial, numbers) # CPU-bound задача
    print(results)
Сводная таблица: Критерий Многопоточность (threading) Многопроцессорность (multiprocessing)
Единица выполнения Поток (Thread) Процесс (Process)
Память Общая Раздельная (требует IPC)
GIL в Python Один на все потоки (ограничивает CPU) Свой GIL на каждый процесс
Идеально для I/O-bound задачи (сеть, файлы) CPU-bound задачи (вычисления)
Накладные расходы Низкие Высокие (запуск процесса, IPC)