Какой тип конкурентность нужно использовать для быстрого выполнения CPU-bound задач?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Конкурентность для CPU-bound задач
Для быстрого выполнения CPU-bound задач в Python нужно использовать multiprocessing, а не threading или asyncio.
Почему multiprocessing?
Проблема в том, что Python имеет Global Interpreter Lock (GIL), который не позволяет нескольким потокам выполняться параллельно в одном процессе. Threading подходит только для I/O-bound операций (сетевые запросы, работа с файлами), где поток может отпустить GIL во время ожидания.
Для CPU-bound работы (вычисления, обработка данных) нужны отдельные процессы с собственными интерпретаторами Python и собственными GIL.
Пример с multiprocessing
from multiprocessing import Pool
import time
def cpu_intensive_task(n):
"""Тяжелые вычисления"""
total = 0
for i in range(n):
total += i ** 2
return total
def main():
# Используем столько процессов, сколько ядер в системе
with Pool() as pool:
# Распределяем работу между процессами
results = pool.map(cpu_intensive_task, [10000000] * 4)
print(results)
if __name__ == "__main__":
main()
Альтернативы
ProcessPoolExecutor — более современный способ:
from concurrent.futures import ProcessPoolExecutor
def main():
with ProcessPoolExecutor(max_workers=4) as executor:
# Отправляем задачи в пул процессов
futures = [executor.submit(cpu_intensive_task, 10000000) for _ in range(4)]
results = [f.result() for f in futures]
print(results)
Важные моменты
- Количество рабочих: обычно выбирают столько, сколько ядер в CPU (
os.cpu_count()) - Overhead коммуникации: multiprocessing имеет оверхед на передачу данных между процессами, так что имеет смысл только для действительно тяжелых задач
- Pickle сериализация: все аргументы должны быть пиклируемы
- если задача очень легкая: оверхед может не стоить выигрыша
Когда использовать что?
- Threading: I/O-bound (сетевые запросы, файлы, БД)
- asyncio: I/O-bound с большим числом одновременных операций
- multiprocessing: CPU-bound вычисления
# Неправильно для CPU-bound
from threading import Thread
threads = [Thread(target=cpu_task) for _ in range(4)]
# GIL не даст реальный параллелизм!
# Правильно для CPU-bound
from multiprocessing import Process
processes = [Process(target=cpu_task) for _ in range(4)]
# Каждый процесс имеет свой GIL — реальный параллелизм
Вывод: для CPU-bound задач всегда используй multiprocessing или ProcessPoolExecutor, это единственный способ получить реальный параллелизм в Python.