← Назад к вопросам
Как можно асинхронно проводить операции в рамках одного процесса?
2.0 Middle🔥 151 комментариев
#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Асинхронные операции в одном процессе Python
В Python существует несколько подходов для выполнения асинхронных операций (одновременно-но-не-параллельно) в одном процессе без создания отдельных потоков или процессов.
1. Модуль asyncio (встроенный, рекомендуется)
asyncio реализует однопоточный конкурентный код с использованием корутин и event loop:
import asyncio
import time
# Определить асинхронную функцию
async def fetch_data(url, delay):
print(f"Начинаю загрузку: {url}")
await asyncio.sleep(delay) # Имитация сетевого запроса
print(f"Завершена загрузка: {url}")
return f"Данные из {url}"
async def main():
# Запустить несколько операций одновременно
tasks = [
fetch_data("https://api.example.com/1", 2),
fetch_data("https://api.example.com/2", 1),
fetch_data("https://api.example.com/3", 3),
]
# Выполнить все параллельно (в одном потоке)
results = await asyncio.gather(*tasks)
return results
# Запустить
results = asyncio.run(main())
print(results)
# Время выполнения: ~3 сек (параллельно), не 6 сек (последовательно)
2. Асинхронные операции с HTTP запросами
import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.json()
async def fetch_multiple_urls(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
return await asyncio.gather(*tasks)
# Использование
urls = [
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/posts/2",
"https://jsonplaceholder.typicode.com/posts/3",
]
results = asyncio.run(fetch_multiple_urls(urls))
for result in results:
print(f"ID: {result['id']}, Title: {result['title']}")
3. Асинхронные операции с БД
import asyncio
import asyncpg # асинхронный драйвер PostgreSQL
async def fetch_users_async():
# Подключиться к БД
conn = await asyncpg.connect('postgresql://user:password@localhost/mydb')
# Параллельно выполнить несколько запросов
tasks = [
conn.fetch('SELECT * FROM users WHERE id = $1', 1),
conn.fetch('SELECT * FROM users WHERE id = $1', 2),
conn.fetch('SELECT * FROM users WHERE id = $1', 3),
]
results = await asyncio.gather(*tasks)
await conn.close()
return results
results = asyncio.run(fetch_users_async())
4. Асинхронные контекстные менеджеры
import asyncio
class AsyncDatabaseConnection:
async def __aenter__(self):
print("Открываю соединение...")
await asyncio.sleep(0.5) # Имитация подключения
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("Закрываю соединение...")
await asyncio.sleep(0.3) # Имитация закрытия
async def query(self, sql):
print(f"Выполняю: {sql}")
await asyncio.sleep(1) # Имитация запроса
return [{"id": 1, "name": "John"}]
async def main():
async with AsyncDatabaseConnection() as db:
result = await db.query("SELECT * FROM users")
print(result)
asyncio.run(main())
5. Async контекстный менеджер для ресурсов
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def async_file(filename, mode):
print(f"Открываю файл: {filename}")
await asyncio.sleep(0.2) # Имитация открытия
class AsyncFile:
async def read(self):
print(f"Читаю {filename}")
await asyncio.sleep(0.5)
return "Содержимое файла"
try:
yield AsyncFile()
finally:
print(f"Закрываю файл: {filename}")
await asyncio.sleep(0.1)
async def main():
async with async_file("data.txt", "r") as f:
content = await f.read()
print(content)
asyncio.run(main())
6. Встроенные асинхронные операции
import asyncio
# asyncio.sleep — неблокирующая задержка
await asyncio.sleep(1)
# asyncio.wait_for — выполнить с timeout
try:
result = await asyncio.wait_for(some_coroutine(), timeout=5.0)
except asyncio.TimeoutError:
print("Таймаут!")
# asyncio.gather — запустить несколько корутин параллельно
results = await asyncio.gather(
coroutine1(),
coroutine2(),
coroutine3(),
return_exceptions=True # Вернуть исключения вместо выброса
)
# asyncio.create_task — создать задачу, которая выполняется в фоне
async def main():
task1 = asyncio.create_task(long_operation())
task2 = asyncio.create_task(another_operation())
# Сделать что-то ещё
await asyncio.sleep(1)
# Дождаться завершения
result1 = await task1
result2 = await task2
7. Обработка ошибок в асинхронном коде
import asyncio
async def failing_operation():
await asyncio.sleep(1)
raise ValueError("Что-то пошло не так")
async def safe_operation():
try:
result = await failing_operation()
except ValueError as e:
print(f"Поймал ошибку: {e}")
return None
async def main():
# Запустить с обработкой исключений
results = await asyncio.gather(
safe_operation(),
safe_operation(),
return_exceptions=True # Не выбросить исключение
)
print(results)
asyncio.run(main())
8. Сравнение подходов
Синхронный (блокирующий) код:
import time
def sync_example():
time.sleep(2) # Блокирует процесс
print("После 2 секунд")
time.sleep(3) # Блокирует снова
print("После ещё 3 секунд")
# Всего: 5 секунд
Асинхронный (неблокирующий) код:
import asyncio
async def async_example():
await asyncio.sleep(2) # Освобождает event loop
print("После 2 секунд")
await asyncio.sleep(3) # Другие операции могут выполняться
print("После ещё 3 секунд")
async def main():
await asyncio.gather(
async_example(), # Начать первый
async_example(), # Начать второй параллельно
)
# Всего: 3 секунды (параллельно), не 10 (последовательно)
asyncio.run(main())
9. Когда использовать асинхронность
- I/O операции: HTTP запросы, чтение файлов, БД
- Многочисленные медленные операции: загрузка 1000+ файлов
- Сервер должен обслуживать много клиентов одновременно
- WebSockets и realtime приложения
10. Когда НЕ использовать
- CPU-bound операции (обработка больших данных, вычисления) — используй
multiprocessing - Простые синхронные скрипты — усложнит код
- Когда можно использовать потоки — быстрее и проще
Заключение
asyncio — мощный инструмент для асинхронных операций в одном процессе. Ключевые преимущества:
- Единственный процесс (меньше памяти)
- Простота (меньше проблем с синхронизацией)
- Идеален для I/O-bound операций