Какой подход к конкурентности в Python наиболее эффективен для CPU-bound задач?

Ответ

Для CPU-bound задач (интенсивных вычислений, таких как обработка данных, математические расчеты) в Python наиболее эффективным подходом является многопроцессорность (multiprocessing).

Почему multiprocessing?

Основная причина — Global Interpreter Lock (GIL). GIL — это мьютекс, который защищает доступ к объектам Python, предотвращая одновременное выполнение байт-кода Python несколькими потоками в рамках одного процесса. Из-за этого многопоточность (threading) не дает прироста производительности для CPU-bound задач, так как потоки выполняются поочередно на одном ядре CPU.

Модуль multiprocessing обходит GIL, создавая отдельные процессы. Каждый процесс имеет свой собственный интерпретатор Python и пространство памяти, что позволяет им выполняться параллельно на разных ядрах процессора.

Пример с multiprocessing.Pool:

import time
from multiprocessing import Pool

def cpu_intensive_task(x):
    # Имитация сложной вычислительной задачи
    result = 0
    for i in range(10**7):
        result += i
    return x * x

if __name__ == '__main__':
    # Используем пул из 4 процессов
    with Pool(4) as p:
        result = p.map(cpu_intensive_task, range(10))
    print(f"Результат: {result}")

Сравнение с другими подходами:

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

Вывод: Для распараллеливания вычислений на несколько ядер CPU в Python используйте multiprocessing или высокоуровневую обертку concurrent.futures.ProcessPoolExecutor.