Какую проблему решает `async/await` в Python?

Ответ

async/await в Python решает проблему блокирующего ввода-вывода (I/O-bound операций) в однопоточном коде. Традиционный синхронный код блокирует выполнение всего потока во время ожидания операций ввода-вывода (например, сетевых запросов, чтения с диска), что приводит к неэффективному использованию ресурсов и низкой пропускной способности.

async/await позволяет писать асинхронный код, который выглядит как синхронный, но при этом кооперативно переключает контекст выполнения, когда встречается await для I/O-операции. Это достигается за счет цикла событий (event loop), который управляет очередью задач.

Ключевые преимущества:

  • Неблокирующий I/O: Поток выполнения не блокируется во время ожидания I/O, позволяя выполнять другие задачи.
  • Высокая производительность: Эффективно обрабатывает тысячи одновременных соединений (например, в веб-серверах) без создания множества потоков.
  • Улучшенная читаемость: Асинхронный код становится более линейным и понятным по сравнению с колбэками.
  • Эффективное использование ресурсов: Один поток может управлять множеством параллельных I/O-операций.

Пример (Python asyncio):

import asyncio
import time

async def fetch_data(delay: int, name: str) -> str:
    """Имитация долгого I/O запроса."""
    print(f"Начинаем {name}...")
    await asyncio.sleep(delay) # Имитация ожидания I/O
    print(f"Закончили {name}.")
    return f"Данные от {name}"

async def main():
    start_time = time.monotonic()
    # Запускаем две I/O-bound задачи параллельно
    task1 = asyncio.create_task(fetch_data(2, "Сервер A"))
    task2 = asyncio.create_task(fetch_data(1, "Сервер B"))

    print("Делаем что-то другое, пока задачи выполняются...")
    # Ожидаем завершения обеих задач
    data1 = await task1
    data2 = await task2

    print(f"Получили: {data1}")
    print(f"Получили: {data2}")
    print(f"Общее время выполнения: {time.monotonic() - start_time:.2f} сек.")

if __name__ == "__main__":
    asyncio.run(main())

В этом примере fetch_data имитирует сетевые запросы. Благодаря async/await и asyncio, обе задачи выполняются конкурентно в одном потоке, и общее время выполнения составляет около 2 секунд (время самой долгой задачи), а не 3 секунды, как было бы при последовательном выполнении.