Функция 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, чтобы выполнить его и получить результат.

Следующий рисунок иллюстрирует ход выполнения программы:

Ход выполнения программы asyncio.create_task()

Другими словами, мы используем 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

Следующий рисунок иллюстрирует ход выполнения:

Пример запуска других задач во время ожидания

Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *