Зачем нужна асинхронность
Асинхронное программирование позволяет выполнять I/O-операции (сетевые запросы, чтение файлов) без блокировки основного потока. Это критически важно для высоконагруженных серверов.
▸Синхронный vs асинхронный
1# Синхронный: каждая операция блокирует поток2import time34def sync_fetch(url):5 time.sleep(2) # Блокировка на 2 секунды6 return f"Data from {url}"78# 3 запроса = 6 секунд9for url in urls:10 result = sync_fetch(url)1112# Асинхронный: операции выполняются параллельно13import asyncio14import aiohttp1516async def async_fetch(url):17 async with aiohttp.ClientSession() as session:18 async with session.get(url) as response:19 return await response.text()2021# 3 запроса = ~2 секунды22async def main():23 tasks = [async_fetch(url) for url in urls]24 results = await asyncio.gather(*tasks)
Основы async/await
▸Корутины
1import asyncio23# Определение корутины4async def greet(name):5 print(f"Hello, {name}")6 await asyncio.sleep(1) # Асинхронная пауза7 print(f"Goodbye, {name}")89# Запуск корутины10asyncio.run(greet("World"))
▸Задачи (Tasks)
1import asyncio23async def fetch_data(url, delay):4 print(f"Starting fetch from {url}")5 await asyncio.sleep(delay)6 return f"Data from {url}"78async def main():9 # Запуск параллельно10 task1 = asyncio.create_task(fetch_data("api1.com", 2))11 task2 = asyncio.create_task(fetch_data("api2.com", 1))1213 # Ожидание результата14 result1 = await task115 result2 = await task21617 print(f"Results: {result1}, {result2}")1819asyncio.run(main())
Модуль asyncio
▸Основные функции
1import asyncio23async def main():4 # gather — запуск нескольких корутин5 results = await asyncio.gather(6 fetch_data("url1"),7 fetch_data("url2"),8 fetch_data("url3")9 )1011 # wait — ожидание с опцией timeout12 done, pending = await asyncio.wait(13 [task1, task2],14 timeout=5.0,15 return_when=asyncio.FIRST_COMPLETED16 )1718 # wait_for — таймаут для одной задачи19 try:20 result = await asyncio.wait_for(fetch_data("url"), timeout=3.0)21 except asyncio.TimeoutError:22 print("Request timed out")2324asyncio.run(main())
▸Задачи с результатами
1import asyncio23async def worker(name, delay):4 await asyncio.sleep(delay)5 return f"{name} done"67async def main():8 tasks = [9 asyncio.create_task(worker(f"Worker {i}", i))10 for i in range(5)11 ]1213 # Сбор результатов14 for task in asyncio.as_completed(tasks):15 result = await task16 print(result)1718asyncio.run(main())
Библиотеки для асинхронности
▸aiohttp (HTTP-клиент)
1import aiohttp2import asyncio34async def fetch(session, url):5 async with session.get(url) as response:6 return await response.json()78async def main():9 async with aiohttp.ClientSession() as session:10 tasks = [fetch(session, url) for url in urls]11 results = await asyncio.gather(*tasks)
▸aiofiles (файловые операции)
1import aiofiles2import asyncio34async def read_file(path):5 async with aiofiles.open(path, 'r') as f:6 content = await f.read()7 return content89async def write_file(path, content):10 async with aiofiles.open(path, 'w') as f:11 await f.write(content)
▸asyncpg (PostgreSQL)
1import asyncpg2import asyncio34async def main():5 conn = await asyncpg.connect('postgresql://user:pass@localhost/db')67 # Выполнение запроса8 rows = await conn.fetch('SELECT * FROM users WHERE active = $1', True)910 # Вставка11 await conn.execute(12 'INSERT INTO users(name, email) VALUES($1, $2)',13 'John', 'john@example.com'14 )1516 await conn.close()
Паттерны асинхронного программирования
▸Очередь задач
1import asyncio23async def producer(queue):4 for i in range(10):5 await queue.put(f"Task {i}")6 await asyncio.sleep(0.5)78async def consumer(queue):9 while True:10 task = await queue.get()11 print(f"Processing: {task}")12 await asyncio.sleep(1)13 queue.task_done()1415async def main():16 queue = asyncio.Queue()1718 # Запуск производителя и потребителей19 producer_task = asyncio.create_task(producer(queue))20 consumers = [asyncio.create_task(consumer(queue)) for _ in range(3)]2122 await producer_task23 await queue.join()2425 for consumer_task in consumers:26 consumer_task.cancel()
▸Семафор для ограничения параллелизма
1import asyncio23async def fetch_with_limit(semaphore, url):4 async with semaphore:5 print(f"Fetching {url}")6 await asyncio.sleep(1)7 return f"Data from {url}"89async def main():10 semaphore = asyncio.Semaphore(5) # Максимум 5 параллельных запросов1112 tasks = [13 fetch_with_limit(semaphore, url)14 for url in urls15 ]1617 results = await asyncio.gather(*tasks)
Ошибки и обработка
1import asyncio23async def risky_operation():4 await asyncio.sleep(1)5 raise ValueError("Something went wrong")67async def main():8 # Обработка ошибок в gather9 results = await asyncio.gather(10 risky_operation(),11 fetch_data("url"),12 return_exceptions=True # Исключения как результаты13 )1415 for result in results:16 if isinstance(result, Exception):17 print(f"Error: {result}")18 else:19 print(f"Success: {result}")
Заключение
async/await в Python — мощный инструмент для создания высокопроизводительных приложений. Понимание event loop, корутин и asyncio критически важно для backend-разработки. На собеседовании спрашивают про разницу между threading и asyncio, когда что использовать, и типичные паттерны.