Когда применяется асинхронность в Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда применяется асинхронность в Python
Асинхронное программирование — это мощный инструмент для создания высокопроизводительных приложений. Но важно понимать, когда его использовать.
Основы асинхронности
Асинхронное программирование позволяет программе продолжать работу, не ожидая завершения I/O операции:
import asyncio
async def fetch_data(url):
print(f'Fetching {url}')
await asyncio.sleep(2) # Имитируем I/O операцию
print(f'Finished {url}')
async def main():
# Запускаем 3 операции одновременно
await asyncio.gather(
fetch_data('https://api1.com'),
fetch_data('https://api2.com'),
fetch_data('https://api3.com'),
)
# Общее время: ~2 сек вместо 6 сек
asyncio.run(main())
Когда использовать асинхронность
1. HTTP запросы и API интеграции
Это самое частое применение асинхронности:
import aiohttp
import asyncio
async def fetch_user_data(user_id):
async with aiohttp.ClientSession() as session:
async with session.get(f'https://api.example.com/users/{user_id}') as response:
return await response.json()
async def main():
# Получить данные 100 пользователей параллельно
user_ids = range(1, 101)
tasks = [fetch_user_data(uid) for uid in user_ids]
users = await asyncio.gather(*tasks)
print(f'Получено {len(users)} пользователей')
asyncio.run(main())
Преимущество: 100 запросов занимают ~1-2 сек вместо 100+ сек последовательно.
2. Работа с базами данных
Асинхронные драйверы БД существуют для всех популярных БД:
import asyncpg # PostgreSQL
async def fetch_users():
conn = await asyncpg.connect('postgresql://user:pass@localhost/db')
try:
# Множество параллельных запросов
tasks = [
conn.fetch('SELECT * FROM users WHERE id = $1', i)
for i in range(1, 101)
]
results = await asyncio.gather(*tasks)
finally:
await conn.close()
asyncio.run(fetch_users())
Асинхронные драйверы: asyncpg (PostgreSQL), motor (MongoDB), redis (Redis), tortoise-orm (ORM).
3. WebSocket соединения и реал-тайм данные
import websockets
import asyncio
async def listen_to_stream():
uri = 'wss://stream.example.com/prices'
async with websockets.connect(uri) as websocket:
while True:
message = await websocket.recv()
print(f'Price update: {message}')
async def main():
# Одновременно слушаем несколько потоков
await asyncio.gather(
listen_to_stream(),
process_orders(),
send_notifications(),
)
asyncio.run(main())
4. Параллельная обработка множества задач
import aiofiles
async def process_file(filename):
async with aiofiles.open(filename, 'r') as f:
content = await f.read()
# Обработать содержимое
return len(content)
async def main():
files = ['file1.txt', 'file2.txt', 'file3.txt']
sizes = await asyncio.gather(*[process_file(f) for f in files])
print(f'Общий размер: {sum(sizes)} байт')
asyncio.run(main())
5. Микросервисы и API гейтвеи
from fastapi import FastAPI
import aiohttp
app = FastAPI()
@app.get('/user/{user_id}')
async def get_user(user_id: int):
# Получить данные из нескольких сервисов параллельно
async with aiohttp.ClientSession() as session:
user_task = fetch_from_service(session, f'http://users-service/users/{user_id}')
orders_task = fetch_from_service(session, f'http://orders-service/orders/{user_id}')
user, orders = await asyncio.gather(user_task, orders_task)
return {'user': user, 'orders': orders}
6. Долгоживущие серверные приложения
import asyncio
async def main():
tasks = [
accept_connections(), # Принимать новые подключения
process_queue(), # Обрабатывать очередь
monitor_health(), # Мониторить здоровье
cleanup_expired(), # Удалять устаревшие данные
]
await asyncio.gather(*tasks)
asyncio.run(main())
Когда НЕ использовать асинхронность
1. CPU-bound операции
# ❌ ПЛОХО: асинхронность не поможет
async def cpu_intensive():
count = 0
for i in range(10**8):
count += i ** 2
return count
# ✅ ХОРОШО: используй multiprocessing
import multiprocessing
with multiprocessing.Pool() as pool:
result = pool.apply_async(cpu_intensive)
Асинхронность помогает при ожидании I/O, а не при вычислениях.
2. Простые синхронные скрипты
# ❌ Излишнее усложнение
async def main():
async with aiohttp.ClientSession() as session:
async with session.get('https://api.github.com') as response:
return await response.json()
# ✅ Просто используй requests
import requests
response = requests.get('https://api.github.com')
data = response.json()
3. Когда библиотеки не асинхронные
# ❌ Блокирует event loop
import time
async def main():
time.sleep(1) # Это заблокирует все остальное!
# ✅ Используй asyncio.sleep или потоки
async def main():
await asyncio.sleep(1) # Не блокирует
Рекомендации и best practices
1. Профилируй перед оптимизацией
import time
import asyncio
start = time.time()
await main() # Асинхронная версия
print(f'Async: {time.time() - start:.2f}s')
2. Используй asyncio.gather для параллельных задач
# Правильно
await asyncio.gather(task1, task2, task3)
# Неправильно
await task1
await task2
await task3
3. Обрабатывай исключения
try:
results = await asyncio.gather(*tasks, return_exceptions=True)
except Exception as e:
print(f'Ошибка: {e}')
4. Не блокируй event loop
# ❌ Плохо
async def main():
import time
time.sleep(1) # Блокирует!
# ✅ Хорошо
async def main():
import asyncio
await asyncio.sleep(1) # Не блокирует
Выводы
Асинхронность — отличный инструмент для I/O-bound операций. Используй её когда:
- Много параллельных I/O операций (HTTP, БД, файлы)
- Нужна высокая пропускная способность
- Есть асинхронные библиотеки для нужных тебе инструментов
- Приложение должно обрабатывать много одновременных соединений
Не используй асинхронность для CPU-bound работы — используй multiprocessing вместо этого.