← Назад к вопросам

Стоит ли использовать многопроцессорность (multiprocessing) в современных проектах?

2.0 Middle🔥 111 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Многопроцессорность в современных проектах Python

Контекст: GIL и параллелизм

Глобальная блокировка интерпретатора (Global Interpreter Lock, GIL) — это одна из основных особенностей CPython. GIL позволяет работать только одному потоку с bytecode интерпретатора одновременно, что делает threading неэффективным для вычислительно-интенсивных задач.

Multiprocessing создаёт отдельные процессы с собственными интерпретаторами Python, каждый со своим GIL, что позволяет добиться настоящего параллелизма на многоядерных системах.

Когда использовать multiprocessing

1. CPU-bound операции (вычисления)

Если задача требует интенсивных вычислений, multiprocessing — лучший выбор:

from multiprocessing import Pool
import time

def cpu_intensive_task(n):
    # Вычисление факториала (CPU-bound)
    result = 1
    for i in range(2, n):
        result *= i
    return result

if __name__ == __main__:
    with Pool(4) as p:
        results = p.map(cpu_intensive_task, [1000, 2000, 3000, 4000])
    print(results)

2. Изоляция критических ошибок

Если процесс падает, он не влияет на другие:

from multiprocessing import Process

def worker(queue):
    try:
        result = 1 / 0  # Ошибка
    except Exception as e:
        queue.put(f"Error: {e}")

if __name__ == __main__:
    from queue import Queue
    q = Queue()
    p = Process(target=worker, args=(q,))
    p.start()
    p.join()
    print(q.get())  # Error: division by zero

Когда НЕ использовать multiprocessing

1. I/O-bound операции (сеть, файлы)

Для I/O используй asyncio или threading — они дешевле по памяти:

# Плохо: multiprocessing для I/O
from multiprocessing import Pool
import requests

def fetch(url):
    return requests.get(url).status_code

# Хорошо: asyncio
import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as resp:
        return resp.status

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        await asyncio.gather(*tasks)

asyncio.run(main())

2. Частая передача данных между процессами

Serialization (pickling) — дорогая операция:

from multiprocessing import Queue
import time

# Неэффективно: много serialization
q = Queue()
for i in range(100000):
    q.put({"data": list(range(1000))})  # Pickling!
    item = q.get()

Современные альтернативы (Python 3.13+)

Отмена GIL в CPython 3.13

NO_GIL режим (экспериментальный) позволяет использовать threading для CPU-bound задач:

import threading

def cpu_task(n):
    result = 1
    for i in range(2, n):
        result *= i
    return result

threads = [
    threading.Thread(target=cpu_task, args=(1000,))
    for _ in range(4)
]

for t in threads:
    t.start()
for t in threads:
    t.join()

# В Python 3.13+ это будет эффективно без GIL

Практический пример: Hybrid подход

from multiprocessing import Pool
import asyncio

# CPU-bound работа в отдельном процессе
def heavy_computation(n):
    return sum(i**2 for i in range(n))

# I/O-bound работа асинхронно
async def fetch_data(url, executor):
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(executor, requests.get, url)
    return result.status_code

async def main():
    # CPU tasks через multiprocessing
    with Pool(2) as pool:
        cpu_results = pool.map(heavy_computation, [1000000, 2000000])
    
    # I/O tasks через asyncio
    from concurrent.futures import ThreadPoolExecutor
    with ThreadPoolExecutor() as executor:
        io_results = await asyncio.gather(
            fetch_data("https://example.com", executor),
            fetch_data("https://python.org", executor)
        )
    
    return cpu_results, io_results

if __name__ == "__main__":
    asyncio.run(main())

Выводы

  1. Используй multiprocessing для CPU-bound задач и критических вычислений
  2. Избегай для I/O (используй asyncio) и частого обмена данными
  3. Мониторь память — каждый процесс требует ~30-50 МБ базовой памяти
  4. Рассмотри asyncio перед multiprocessing — часто проще и эффективнее
  5. Python 3.13+ меняет ситуацию с GIL — threading может стать конкурентным
Стоит ли использовать многопроцессорность (multiprocessing) в современных проектах? | PrepBro