Вопрос проверяет понимание подходов к конкурентности в Python, помимо асинхронности, и знание их применения для эффективного решения задач параллельной обработки.
Конкурентность в Python — это способность программы выполнять несколько задач "одновременно", даже если физически они могут выполняться не строго параллельно. Асинхронность с asyncio — один из популярных подходов, но не единственный. Другие основные альтернативы — это многопоточность (threading) и многопроцессность (multiprocessing).
Модуль threading позволяет создавать и управлять потоками выполнения. Потоки разделяют память процесса, что делает обмен данными между ними относительно простым. Однако из-за Global Interpreter Lock (GIL) в CPython только один поток может выполнять байт-код Python в любой момент времени. Это делает многопоточность неэффективной для CPU-связанных задач, но она хорошо подходит для I/O-связанных операций (например, сетевые запросы, чтение файлов), где потоки могут освобождать GIL во время ожидания.
import threading
import time
def task(name):
print(f"{name} started")
time.sleep(2) # Имитация I/O-операции
print(f"{name} finished")
threads = []
for i in range(3):
t = threading.Thread(target=task, args=(f"Thread-{i}",))
threads.append(t)
t.start()
for t in threads:
t.join()
print("All threads done")Модуль multiprocessing создаёт отдельные процессы, каждый со своим интерпретатором Python и памятью, что позволяет обойти ограничения GIL. Это делает его идеальным для CPU-связанных задач, которые требуют настоящего параллелизма на многоядерных процессорах. Недостаток — более высокая накладная нагрузка на создание процессов и сложность обмена данными между ними (требуются механизмы вроде очередей или разделяемой памяти).
import multiprocessing
import math
def compute_square(n):
return n * n
if __name__ == "__main__":
numbers = [1, 2, 3, 4, 5]
with multiprocessing.Pool(processes=2) as pool:
results = pool.map(compute_square, numbers)
print(f"Squares: {results}")Модуль concurrent.futures предоставляет абстракцию пула потоков (ThreadPoolExecutor) и пула процессов (ProcessPoolExecutor), упрощая выполнение конкурентных задач. Это позволяет легко переключаться между потоками и процессами в зависимости от характера задачи.
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
def fetch_url(url):
resp = requests.get(url)
return len(resp.content)
urls = ["https://httpbin.org/delay/1", "https://httpbin.org/delay/2"]
with ThreadPoolExecutor(max_workers=3) as executor:
futures = [executor.submit(fetch_url, url) for url in urls]
for future in as_completed(futures):
print(f"Fetched {future.result()} bytes")Вывод: Выбор альтернативы зависит от типа задачи. Используйте многопоточность для I/O-операций, где важно лёгкое разделение данных. Применяйте многопроцессность для CPU-интенсивных вычислений, требующих настоящего параллелизма. Модуль concurrent.futures предлагает удобный высокоуровневый способ для обоих сценариев.
Уровень
Рейтинг:
3
Сложность:
5
Навыки
Python
Node.js
Ключевые слова
Подпишись на Python Developer в телеграм