Вопрос проверяет понимание двух основных подходов к параллельному выполнению задач в Python и помогает оценить, когда использовать многопоточность, а когда асинхронность.
В Python существует два основных подхода для организации параллельного выполнения задач, связанных с ожиданием: многопоточность (threading) и асинхронное программирование с async/await. Хотя оба подхода позволяют выполнять несколько операций "одновременно", они работают на разных принципах и имеют разные области применения.
Модуль threading создает потоки выполнения внутри одного процесса. Потоки управляются операционной системой, которая сама решает, когда переключаться между ними (вытесняющая многозадачность). Однако в Python существует GIL (Global Interpreter Lock), который предотвращает одновременное выполнение Python-байткода несколькими потоками в одном процессе. Это означает, что для CPU-интенсивных задач (CPU-bound) многопоточность не даст прироста производительности на многоядерных системах. Потоки полезны для задач, связанных с ожиданием ввода-вывода (I/O-bound), например, чтения файлов или сетевых запросов, потому что пока один поток ждет ответа, GIL может быть передан другому потоку, который готов выполнять работу.
Асинхронное программирование, реализованное в модуле asyncio, использует модель кооперативной многозадачности. Здесь существует только один поток, но множество задач (coroutines). Задача явно передает управление (используя ключевое слово await), когда ей нужно дождаться результата, например, ответа от сети. Это позволяет эффективно использовать время простоя, планируя выполнение других готовых задач. Такой подход создает значительно меньше накладных расходов, чем создание и переключение потоков ОС, и может обслуживать тысячи одновременных соединений.
Пример с threading для скачивания нескольких URL:
import threading
import requests
def download(url):
response = requests.get(url)
print(f"Downloaded {url}: {len(response.content)} bytes")
urls = ["https://example.com", "https://example.org"]
threads = []
for url in urls:
t = threading.Thread(target=download, args=(url,))
t.start()
threads.append(t)
for t in threads:
t.join()
print("All downloads finished.")Пример с asyncio для той же задачи:
import asyncio
import aiohttp
async def download(session, url):
async with session.get(url) as response:
content = await response.read()
print(f"Downloaded {url}: {len(content)} bytes")
async def main():
async with aiohttp.ClientSession() as session:
urls = ["https://example.com", "https://example.org"]
tasks = [download(session, url) for url in urls]
await asyncio.gather(*tasks)
print("All downloads finished.")
asyncio.run(main())time.sleep или requests.get без специальных оберток), так как они заблокируют весь цикл событий.Вывод: Используйте threading для простых I/O-задач, особенно когда нужно интегрироваться с библиотеками, которые не поддерживают asyncio. Async/await предпочтительнее для высокопроизводительных сетевых приложений (веб-серверы, клиенты API, чат-боты), где требуется обрабатывать тысячи одновременных соединений с минимальными затратами ресурсов.
Уровень
Рейтинг:
4
Сложность:
6
Навыки
Python
Node.js
Ключевые слова
Подпишись на Python Developer в телеграм