нюансы await в asyncio

Подскажите, пожалуйста, новичку нюансы работы await в asyncio. Пример кода.

import asyncio

async def some_task():
    print("Задача начата")
    await asyncio.sleep(2)
    print("Задача завершена")

async def main():

    task = asyncio.create_task(some_task())
    #await asyncio.sleep(5)
    await task
    #await asyncio.sleep(5)


asyncio.run(main())
  1. Если я просто запускаю await task, тут все понятно, задача выполняется 2 сек
  2. Если я перед await task ставлю await asyncio.sleep(5), то общее время выполнения программы 5 сек, задачи внутри программы - 2 сек
  3. Если я после await task ставлю await asyncio.sleep(5), то общее время выполнения программы около 7 сек, задачи внутри программы - 2 сек

Почему в одном случае 2 задачи выполняются "параллельно", а в другом "последовательно"? Заранее спасибо.


Ответы (1 шт):

Автор решения: CrazyElf

Давайте посмотрим на описанные вами варианты кода:

task = asyncio.create_task(some_task()) # задача запустилась
await asyncio.sleep(5)                  # ждём 5с в спячке
await task                              # без ожидания, т.к. задача уже выполнилась
#await asyncio.sleep(5)

В тот момент, когда вы начинаете ждать 5 секунд задача task уже работает, вы её запустили через asyncio.create_task. Поэтому, когда вы начинаете ожидать task через 5с, она уже выполнилась. Таким образом и получается, что 2с работы task произошли в тот момент, когда вы делали sleep и прибавлять эти 2с не нужно, общее время совпадает со временем sleep. Задачи эти выполнялись параллельно (если для этого хватило потоков, а обычно их хватает).

task = asyncio.create_task(some_task()) # задача запустилась
#await asyncio.sleep(5)
await task                              # ждём окончания задачи 2с
await asyncio.sleep(5)                  # ждём ещё 5с в спячке

В этом же случае получается, что вы сначала ждёте задачу task 2с и только потом начинаете ждать sleep 5с, вот и получается 2с ожидания + 5с ожидания = 7с.

Мораль такая: старайтесь по возможности ждать задачи не последовательно, а параллельно. Те задачи, которые могут работать одновременно, собирайте в список и ждите их сразу все вместе, например, через await asyncio.wait. И делайте это тогда, когда без результата работы этих задач уже нельзя двигаться по коду дальше. А до этого их не нужно ждать, создайте их и пусть работают в фоне. И только те задачи, которые должны выполняться строго последовательно - вот их ждите по очереди, через отдельный await у каждой такой задачи.

→ Ссылка