Корутина ещё не означает запущенную работу
Вызов функции, объявленной через async def, создаёт объект корутины. Сам по себе такой вызов не выполняет её тело. Корутина должна быть ожидаема через await или запланирована подходящим средством asyncio. Если сначала полностью ожидать одну операцию, а затем начинать вторую, получится последовательность. Для независимых операций можно заранее создать задачи, чтобы периоды ожидания перекрывались. В Python 3.11 появился TaskGroup: он позволяет объединить связанные задачи и дождаться их завершения при выходе из своего контекста. Это способ организовать работу, а не обещание определённого ускорения.
Найдите участок, который занимает цикл событий
В одном потоке цикла событий в каждый конкретный момент выполняется одна задача. Другие получают возможность продолжить работу, когда текущая приостанавливается. Наличие await в тексте ещё не гарантирует паузу при каждом выполнении: ожидаемый результат может быть уже готов. Длительный обычный расчёт внутри корутины продолжит занимать поток. Аналогично блокирующее ожидание через time.sleep задержит другие задачи этого цикла. Для асинхронной учебной паузы используют asyncio.sleep. Настоящие блокирующие операции требуют отдельного решения: подходящей асинхронной библиотеки либо вынесения работы из потока цикла событий.
Проверьте поведение на независимых паузах
Придумайте три условные операции, каждая из которых сначала сообщает о начале, затем ждёт одну секунду и сообщает о завершении. Здесь пауза лишь моделирует ожидание; она не изображает вычисление и не проверяет скорость внешнего сервиса. При последовательном запуске суммарное время будет около трёх секунд. При совместном планировании задач их ожидания смогут перекрыться, поэтому время приблизится к одной секунде с накладными расходами. Смотрите не только на секундомер, но и на порядок сообщений. Если все начала появились до первых завершений, вы получили наглядное свидетельство перекрытия работы в этом опыте.
Добавьте наблюдение и правильное завершение
В обычном самостоятельном сценарии точкой входа может служить asyncio.run: она управляет циклом событий и завершает связанные служебные ресурсы. Её нельзя вызывать в том же потоке, где уже работает другой цикл. Поэтому поведение интерактивной среды нужно учитывать отдельно. Для поиска ошибок полезен режим отладки: он помогает замечать забытые ожидания корутин и медленные обратные вызовы. Проверяйте также исключения и завершение задач. Если результат нужен дальше, ошибка не должна теряться в фоновом запуске. Прежде чем наращивать количество задач, определите, кто ждёт их результат и как освобождаются используемые ресурсы.
Попробуйте на практике
Сравните последовательное и совместное ожидание трёх локальных операций в отдельном сценарии Python 3.11 или новее.
- Создайте асинхронную учебную функцию с меткой, сообщениями о начале и конце и паузой через asyncio.sleep длительностью одну секунду.
- В первой версии главной корутины последовательно дождитесь трёх вызовов и измерьте общую длительность.
- Во второй версии создайте три задачи внутри TaskGroup, сохранив одинаковые паузы и сообщения.
- Сопоставьте время и порядок сообщений, затем объясните, какие участки ожидания перекрылись.
- Включите режим отладки при запуске и убедитесь, что нет предупреждений о забытых корутинах или потерянных исключениях.
Как проверить результат. Последовательная версия занимает примерно сумму пауз, совместная примерно длительность одной. Вы объясняете разницу организацией ожидания, а не ускорением вычислений процессора.
Частые вопросы
Подходит ли asyncio для ускорения тяжёлого обычного цикла вычислений?
Одно добавление async этого не делает. Сначала оценивают характер работы; вычисления могут потребовать оптимизации или выполнения в другом процессе.
Почему времени потребовалось немного больше одной секунды?
Таймеры, планирование, вывод и нагрузка среды добавляют задержки. В учебном опыте важно различие схем выполнения, а не совпадение времени до миллисекунды.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.