Ответ
Для тяжелых, CPU-bound вычислений в Python однозначно следует использовать многопроцессность (multiprocessing
).
Почему не многопоточность?
Ключевая причина — Global Interpreter Lock (GIL). Это механизм в стандартном интерпретаторе CPython, который позволяет только одному потоку исполнять Python-байткод в один момент времени. В результате, многопоточность (threading
) не дает прироста производительности на многоядерных процессорах для задач, интенсивно использующих CPU.
Преимущества multiprocessing
- Обход GIL: Каждый процесс получает собственный интерпретатор Python и область памяти, поэтому GIL одного процесса не мешает другим.
- Полная утилизация CPU: Позволяет по-настоящему параллельно выполнять код на всех доступных ядрах процессора.
Сравнение:
multiprocessing
: Идеально для CPU-bound задач (математические расчеты, обработка данных).threading
: Эффективна для I/O-bound задач (ожидание ответа от сети, чтение с диска), так как GIL освобождается на время ожидания ввода-вывода.
Пример распараллеливания вычислений с multiprocessing.Pool
:
import multiprocessing
import time
def heavy_computation(number):
# Имитация сложной вычислительной задачи
result = 0
for i in range(10**7):
result += i
return number * number
if __name__ == "__main__":
# Конструкция if __name__ == "__main__" обязательна
# для корректной работы multiprocessing в некоторых ОС
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
start_time = time.time()
# Создаем пул из 4 процессов
with multiprocessing.Pool(processes=4) as pool:
results = pool.map(heavy_computation, numbers)
print(f"Результаты: {results}")
print(f"Время выполнения: {time.time() - start_time:.2f} секунд")
# Вывод (примерный):
# Результаты: [1, 4, 9, 16, 25, 36, 49, 64]
# Время выполнения: 2.15 секунд
# (Без пула это заняло бы ~8 секунд на 1 ядре)
Вывод: Используйте multiprocessing
для вычислений и threading
для ожидания.