Функция asyncio.create_task() в Python — использование на примерах
В этом руководстве вы узнаете, как использовать функцию asyncio.create_task() для одновременного запуска нескольких задач в Python.
Имитация длительной операции
Чтобы смоделировать длительную операцию, вы можете использовать сопрограмму sleep() пакета asyncio. Функция sleep() задерживает указанное количество секунд:
await asyncio.sleep(seconds)
Поскольку Sleep() — это сопрограмма, вам необходимо использовать ключевое слово await. Например, в следующем примере используется сопрограмма sleep() для имитации вызова API:
import asyncio async def call_api(message, result=1000, delay=3): print(message) await asyncio.sleep(delay) return result
Call_api() — это сопрограмма. Она отображает сообщение, приостанавливает выполнение на указанное количество секунд (по умолчанию — три секунды) и возвращает результат.
Следующая программа использует call_api() дважды и измеряет время, необходимое для завершения:
import asyncio import time async def call_api(message, result=1000, delay=3): print(message) await asyncio.sleep(delay) return result async def main(): start = time.perf_counter() price = await call_api('Get stock price of GOOG...', 300) print(price) price = await call_api('Get stock price of APPL...', 400) print(price) end = time.perf_counter() print(f'It took {round(end-start,0)} second(s) to complete.') asyncio.run(main())
Выход:
Get stock price of GOOG... 300 Get stock price of APPL... 400 It took 6.0 second(s) to complete.
Как это работает (фокусируясь на сопрограмме main()):
- Сначала запустите таймер для измерения времени, используя функцию perf_counter() модуля time:
start = time.perf_counter()
- Во-вторых, вызовите сопрограмму call_api() и отобразите результат:
price = await call_api('Get stock price of GOOG...', 300) print(price)
- В-третьих, вызовите call_api() второй раз:
price = await call_api('Get stock price of APPL...', 400) print(price)
- Наконец, покажите время, необходимое программе для завершения:
end = time.perf_counter() print(f'It took {round(end-start,0)} second(s) to complete.')
Потому что каждый вызов call_api() занимает три секунды, а двойной вызов — шесть секунд.
В этом примере мы вызываем сопрограмму напрямую и не помещаем ее в цикл событий для запуска. Вместо этого мы получаем объект сопрограммы и используем ключевое слово await, чтобы выполнить его и получить результат.
Следующий рисунок иллюстрирует ход выполнения программы:
Другими словами, мы используем async и await для написания асинхронного кода, но не можем запускать его одновременно. Чтобы одновременно выполнять несколько операций, нам нужно использовать так называемые задачи (tasks).
Что такое task в Python?
Task — это оболочка сопрограммы, которая планирует запуск сопрограммы в цикле событий как можно скорее. Планирование и выполнение происходят неблокирующим способом. Другими словами, вы можете создать task и мгновенно выполнить другой код во время выполнения задачи.
Обратите внимание, что task отличается от ключевого слова await, которое блокирует всю сопрограмму до тех пор, пока операция не завершится с результатом. Важно, чтобы вы могли создавать несколько задач и планировать их одновременный запуск в цикле событий.
Чтобы создать задачу, вы передаете сопрограмму функции create_task() пакета asyncio. Функция create_task() возвращает объект Task.
Следующая программа показывает, как создать две задачи, которые планируют и выполняют сопрограмму call_api():
import asyncio import time async def call_api(message, result=1000, delay=3): print(message) await asyncio.sleep(delay) return result async def main(): start = time.perf_counter() task_1 = asyncio.create_task( call_api('Get stock price of GOOG...', 300) ) task_2 = asyncio.create_task( call_api('Get stock price of APPL...', 300) ) price = await task_1 print(price) price = await task_2 print(price) end = time.perf_counter() print(f'It took {round(end-start,0)} second(s) to complete.') asyncio.run(main())
Выход:
Get stock price of GOOG... Get stock price of APPL... 300 300 It took 3.0 second(s) to complete.
Как это работает.
- Сначала запустите таймер:
start = time.perf_counter()
- Затем создайте задачу и запланируйте ее немедленный запуск в цикле событий:
task_1 = asyncio.create_task( call_api('Get stock price of GOOG...', 300) )
- Затем создайте еще одну задачу и запланируйте ее немедленный запуск в цикле событий:
task_2 = asyncio.create_task( call_api('Get stock price of APPL...', 400) )
- После этого дождитесь выполнения задач:
price = await task_1 print(price) price = await task_2 print(price)
Важно использовать ключевое слово await для ожидания выполнения задач в какой-то момент программы. Если бы мы не использовали ключевое слово await, Python запланировал бы запуск задачи, но остановил бы ее, когда asyncio.run() завершит цикл событий.
Следующий рисунок иллюстрирует ход выполнения программы:
- Наконец, покажите время, необходимое для выполнения функции main():
end = time.perf_counter() print(f'It took {round(end-start,0)} second(s) to complete.')
Используя функцию create_task(), программа работает намного быстрее. Чем больше задач вы выполняете, тем быстрее это происходит.
Запуск других задач во время ожидания
Когда call_api работает, вы можете запускать другие задачи. Например, следующая программа отображает сообщение каждую секунду во время ожидания задач call_api:
import asyncio import time async def call_api(message, result=1000, delay=3): print(message) await asyncio.sleep(delay) return result async def show_message(): for _ in range(3): await asyncio.sleep(1) print('API call is in progress...') async def main(): start = time.perf_counter() message_task = asyncio.create_task( show_message() ) task_1 = asyncio.create_task( call_api('Get stock price of GOOG...', 300) ) task_2 = asyncio.create_task( call_api('Get stock price of APPL...', 300) ) price = await task_1 print(price) price = await task_2 print(price) await message_task end = time.perf_counter() print(f'It took {round(end-start,0)} second(s) to complete.') asyncio.run(main())
Выход:
Get stock price of GOOG... Get stock price of APPL... API call is in progress... API call is in progress... API call is in progress... 300 300
Следующий рисунок иллюстрирует ход выполнения: