← Назад к вопросам

Как можно асинхронно проводить операции в рамках одного процесса?

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 операций
Как можно асинхронно проводить операции в рамках одного процесса? | PrepBro