аватар question@mail.ru · 01.01.1970 03:00

Python когда следует использовать async, а когда await?

Когда следует использовать async, а когда await?

аватар answer@mail.ru · 01.01.1970 03:00

Попытаюсь дополнить ответ @ext.

async/await - это оптимизация, т.е. использовать их категорически не надо там, где все и так работает хорошо. Ведь это усложняет логику исполнения программы и тянет за собой много всего. Потому что если решились использовать асинхронную функциональность - будьте добры использовать ТОЛЬКО библиотеки, которые это поддерживают.

Исполнение асинхронного кода подобно исполнению многопоточного кода на одноядерном процессоре. Так же существуют несколько задач, которые могут быть исполнены одновременно и есть контекст исполнения каждой задачи. Однако при многопоточности процессор сам по таймеру решает, когда переключать контекст и заниматься исполнением другой задачи.

Асинхронный подход предполагает, что в само приложение задает специальные места, где такое переключение возможно. В Python в asyncio этими местами становятся вызовы с await.

Если решились оптимизировать, то в первую очередь стоит вспомнить что такое и чем различаются CPU-bound и IO-bound операции. Первые постоянно задействуют ресурсы процессора, все время что-то считают и вычисляют. Вторые большую часть времени ожидают операций ввода/вывода от файловой системы, сети или еще невесть чего.

Из-за GIL в Python (CPython) CPU-bound операции невозможно оптимизировать с использованием потоков, только вынесением в отдельный процесс. Совершенно то же самое ограничение накладывается на использование асинхронного подхода, потому что асинхронный код преимущественно одноядерный и если один исполнитель будет переключаться с одного процесса на другой, где его присутствие одинаково важно - никакого толку не будет (будет гораздо хуже).

Другое дело IO-bound задачи. Яркими примерами могут служить - чтение файлов, запрос в базу данных, даже time.sleep (который часто используется для имитации сложных IO-bound операций и есть специальный asyncio.sleep).

Например:

def create_report(cursor, *args, **kwargs):    # выполнить какую-то подготовительную работу    data = cursor.execute(sql_query)    # обработать полученные данные и сформировать некий отчет

Здесь функция execute не сильно нагрузит процессор, больше будет ждать ответа от базы данных. Поэтому процессору позволительно заняться в этот момент чем-нибудь другим. Но этот код синхронный, приходится ждать. Если нам надо построить очень много таких отчетов, то мы можем решиться оптимизировать это с использованием asyncio. Давайте попробуем.

Сначала нам придется выбросить старый клиент для похода в базу данных и использовать специальный асинхронный, который будет осуществлять неблокирующий вызов.

async def create_report(cursor, *args, **kwargs):    # cursor - это уже не тот cursor, что из предыдущего примера    # выполнить какую-то подготовительную работу    data = await cursor.execute(sql_query)    # обработать полученные данные и сформировать некий отчет

Любая функция, которая использует await обязана объявляться как async - тут нет выбора. А await объявляет точку в программе, в которой мы можем переключиться на другую полезную работу, если такая есть, потому что ресурсы процессора здесь не нужны. Таким образом мы сможем успеть больше.

Оставляю за скобками инициализацию event-loop и прочие прелести запуска асинхронных программ.

Очень важно - сказал ""А"" (async), говори ""B"" (await), друг без друга они не существуют.

Крайне внятной я нашел следующую информацию:

Не знаю, есть ли в интернете перевод этого добра, но если нужен, я мог бы подготовить.

Напоследок, интересное утверждение: ""используйте асинхронность, когда можете, а потоки - когда должны"".

Последние

Похожие