Вопрос проверяет понимание различий между CPU-bound и IO-bound задачами и умение выбирать подходящий подход к параллелизму в Python.
В Python для параллельного выполнения кода доступны несколько подходов: многопроцессность (multiprocessing), многопоточность (threading) и асинхронное программирование (asyncio). Выбор между ними зависит от природы задачи — является ли она ограниченной процессором (CPU-bound) или вводом-выводом (IO-bound).
IO-bound задачи — это операции, время выполнения которых в основном тратится на ожидание внешних ресурсов. К ним относятся:
Во время такого ожидания процессор простаивает, и его можно использовать для выполнения других задач.
Модуль multiprocessing создаёт отдельные процессы операционной системы. У каждого процесса:
Для IO-bound задач создание полноценного процесса — это "стрельба из пушки по воробьям". Пока один процесс ждёт ответа от сети, он всё равно занимает память и ресурсы ОС, а переключение между процессами (контекстный переключатель) тяжелее, чем между потоками.
Для IO-bound задач в Python используют:
Рассмотрим задачу выполнения нескольких HTTP-запросов.
import time
import requests
from multiprocessing import Pool
from threading import Thread
import asyncio
import aiohttp
# Синхронный (последовательный) подход
def sync_fetch(urls):
for url in urls:
response = requests.get(url)
print(f"Fetched {len(response.content)} bytes")
# Многопроцессный подход (multiprocessing)
def mp_fetch(urls):
with Pool(processes=4) as pool:
pool.map(requests.get, urls)
# Многопоточный подход (threading)
def thread_fetch(urls):
threads = []
for url in urls:
t = Thread(target=requests.get, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join()
# Асинхронный подход (asyncio)
async def async_fetch(urls):
async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
task = asyncio.create_task(session.get(url))
tasks.append(task)
responses = await asyncio.gather(*tasks)
for resp in responses:
print(f"Fetched {resp.content_length} bytes")
# Запуск асинхронной функции
# asyncio.run(async_fetch(urls))
В этом примере multiprocessing будет самым медленным и ресурсоёмким для сетевых запросов из-за накладных расходов на процессы.
Вывод: Multiprocessing стоит применять для тяжёлых вычислений (CPU-bound), таких как обработка изображений или сложные математические расчёты, где нужно задействовать несколько ядер CPU. Для задач, связанных с ожиданием (сеть, диск, пользователь), всегда предпочтительнее использовать многопоточность или, что ещё лучше, асинхронное программирование.
Уровень
Рейтинг:
3
Сложность:
5
Навыки
Python
Node.js
Ключевые слова
Подпишись на Python Developer в телеграм