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

Для вычислительной задачи лучше задействовать несколько процессов или несколько потоков

2.3 Middle🔥 221 комментариев
#Асинхронность и многопоточность

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

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

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

Для вычислительной задачи лучше задействовать несколько процессов или потоки

Коротко: ПРОЦЕССЫ для CPU-bound, ПОТОКИ для I/O-bound.

CPU-bound vs I/O-bound

# CPU-bound: вычисления, обработка данных
def cpu_work(n):
    return sum(i*i for i in range(n))

cpu_work(10**8)  # Медленно, требует CPU

# I/O-bound: сетевые запросы, файловые операции
import requests
response = requests.get('https://example.com')  # Быстро ждёшь ответ

Потоки (threads)

Потоки используют одно ядро из-за GIL (Global Interpreter Lock).

import threading
import time

def cpu_work():
    return sum(i*i for i in range(10**8))

start = time.time()
t1 = threading.Thread(target=cpu_work)
t2 = threading.Thread(target=cpu_work)
t1.start(); t2.start()
t1.join(); t2.join()
print(f"Threads: {time.time() - start:.1f}s")  # 20s (медленнее!)

Почему медленно:

  • GIL позволяет работать только одному потоку одновременно
  • Переключение контекста добавляет overhead
  • Результат: медленнее чем последовательно!

Процессы (multiprocessing)

Процессы используют несколько ядер (нет GIL).

from multiprocessing import Process
import time

def cpu_work():
    return sum(i*i for i in range(10**8))

start = time.time()
p1 = Process(target=cpu_work)
p2 = Process(target=cpu_work)
p1.start(); p2.start()
p1.join(); p2.join()
print(f"Processes: {time.time() - start:.1f}s")  # 10s (в 2x быстрее!)

Сравнение

Задача: sum(i*i for i in range(10**8))

Последовательно:     10s
1 поток:             10s (GIL)
2 потока:            20s (GIL + overhead)
2 процесса:          5s (настоящий параллелизм!)

CPU-bound: используй процессы

from multiprocessing import Pool

def calculate(n):
    return sum(i*i for i in range(10**7))

# Используй Pool
with Pool(4) as pool:
    results = pool.map(calculate, [1, 2, 3, 4, 5, 6, 7, 8])

# 4 процесса параллельно на 4-ядерной машине

I/O-bound: используй потоки или asyncio

import threading
import requests

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

# Потоки
threads = [threading.Thread(target=fetch, args=(url,)) for url in urls]
for t in threads: t.start()

# ИЛИ asyncio (лучше)
import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

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

Практические примеры

Пример 1: Обработка больших файлов (CPU-bound)

from multiprocessing import Pool
import numpy as np

def process_chunk(chunk):
    # Обработка (медленно)
    return np.sum(chunk ** 2)

# Читаем большой файл чанками
chunks = read_file_in_chunks('huge_data.bin', chunk_size=10**6)

# Используем несколько процессов
with Pool(4) as pool:
    results = pool.map(process_chunk, chunks)

total = sum(results)

Пример 2: Скачивание файлов (I/O-bound)

import concurrent.futures
import requests

def download(url):
    response = requests.get(url)
    return len(response.content)

urls = ['http://example.com/file1.zip', ...]

# Используем потоки
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
    results = executor.map(download, urls)

Пример 3: Веб-скрейпинг (I/O-bound)

import asyncio
import aiohttp

async def scrape(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

urls = ['http://example.com/page1', ...]

# asyncio для IO-bound
results = asyncio.run(
    asyncio.gather(*[scrape(url) for url in urls])
)

Проблемы каждого подхода

Потоки (threads)

Плюсы:

  • Легко использовать
  • Хорошо для I/O
  • Меньше overhead чем процессы

Минусы:

  • GIL убивает CPU-bound
  • Race conditions
  • Deadlocks возможны

Процессы (multiprocessing)

Плюсы:

  • Истинный параллелизм для CPU
  • Нет GIL
  • Масштабируется на многоядрах

Минусы:

  • Медленнее запускаются (форк процесса)
  • Больше памяти (каждый процесс - отдельный Python)
  • Сложнее share данные
  • IPC overhead

Asyncio

Плюсы:

  • Очень быстро для I/O
  • Низкий overhead
  • Масштабируется на тысячи connections

Минусы:

  • Не работает для CPU-bound
  • Требует async/await везде
  • Понять сложнее

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

CPU-bound (вычисления):
├─ Малое количество: Использовать процессы (multiprocessing)
├─ Большое: Использовать Cython или NumPy на C
└─ Критичное: Использовать Rust/C с Python биндингами

I/O-bound (сетевые операции):
├─ Быстрое решение: asyncio
├─ Интеграция с blocking code: потоки
└─ Микросервисы: потоки/asyncio на разных сервисах

Реальный пример: выбор технологии

# Задача: Обработать 1000 изображений

# Вариант 1: Потоки (медленно!)
with ThreadPoolExecutor(max_workers=10) as executor:
    executor.map(process_image, images)
# Время: 100s (GIL блокирует)

# Вариант 2: Процессы (быстро!)
with Pool(4) as pool:
    pool.map(process_image, images)
# Время: 25s (4x ядра работают)

# Вариант 3: GPU (если есть)
# Время: 5s (специализированное оборудование)

Как я выбираю

  1. Профилирую задачу

    • Сколько времени на CPU vs I/O?
  2. Если 90% CPU:

    • Используй multiprocessing
    • Или Cython/NumPy
  3. Если 90% I/O:

    • Используй asyncio
    • Или потоки если нет выбора
  4. Если смешанное:

    • Комбинирую подходы
    • Asyncio для I/O + процессы для CPU

Инструменты для профилирования

import cProfile
import pstats

# Профилирование
cProfile.run('my_function()')

# Или более детально
from line_profiler import profile

@profile
def slow_function():
    pass

Заключение

  • CPU-bound → многопроцессность
  • I/O-bound → asyncio или потоки
  • Профилируй перед оптимизацией
  • GIL убивает CPU-bound потоки
  • asyncio лучше для I/O чем потоки
  • Иногда Cython/NumPy лучше чем multiprocessing
Для вычислительной задачи лучше задействовать несколько процессов или несколько потоков | PrepBro