Проблема потока выполнения Asyncio - PullRequest
2 голосов
/ 29 марта 2020

Я немного новичок в asyncio в python. Я пытался запустить этот простой код, но я не знаю, почему я получаю этот неожиданный вывод.

Что я сделал, так это то, что в функции outer я создал асин c задач и сохранил их в массиве tasks. Прежде чем ждать выполнения этих задач, я написал оператор печати print("outer"), который должен выполняться на каждой итерации. А внутри задачи я написал еще одно выражение print print("inner") в функции inner. Но каким-то образом я получаю неожиданный вывод.

Вот код -

import asyncio


def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(outer(loop))
    loop.close()


async def outer(loop):
    tasks = []
    for i in range(0, 5):
        tasks.append(loop.create_task(inner()))

    for task in tasks:
        print("outer")
        await task


async def inner():
    print("inner")
    await asyncio.sleep(0.5)

if __name__ == '__main__':
    main()

Вот вывод -

outer
inner
inner
inner
inner
inner
outer
outer
outer
outer

Мой ожидаемый вывод -

outer
inner
outer
inner
outer
inner
outer
inner
outer
inner

Почему все inner печатают до outer. Каков правильный ход выполнения asyncio. Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 30 марта 2020

В async def outer(loop)

for i in range(0, 5):
    tasks.append(loop.create_task(inner()))
  • Пять новых inner задач созданы, запланированы и запущены.
  • событие l oop запускает запланированные задачи до они завершены.

Если вы добавите немного больше к inner и outer, это лучше покажет этот процесс:

async def outer(loop):
    tasks = []
    for i in range(0, 5):
        tasks.append(loop.create_task(inner(i)))
    await asyncio.sleep(3)
    for task in tasks:
        print('outer')
        await task

async def inner(n):
    print(f"inner {n} start")
    await asyncio.sleep(0.5)
    print(f'inner {n} end')
  • в то время как outer спит

    • Первая задача запускает до своего оператора ожидания
    • Событие l oop приостанавливает выполнение первой задачи
    • Событие l oop запускает следующую запланированную задачу вплоть до ее оператора await
    • Это продолжается до тех пор, пока каждая задача не сможет выполнить свою инструкцию await, а затем событие l oop начинает оглядываться, чтобы увидеть, выполнены ли какие-либо задачи в ожидании того, что они ожидали - если это выполнено, это позволяет им запускаться еще немного.
    • Это продолжается до тех пор, пока все задачи не будут выполнены
  • Вы видите, что пять задач выполняются и заканчиваются sh перед второй для l oop даже запускается.

  • в секунду для l oop каждая задача уже выполнена, поэтому await task ждать нечего и outer печатается пять раз подряд.

Я немного запутался в событии l oop, контролирующем все - я не нашел никакой явной документации для этого - вероятно, на это ссылаются в create_task документах: Wrap the coro coroutine into a Task and schedule its execution. Когда вы создаете задание, которое это запланировано. Я видел видео на pyvideo.org, где показан этот процесс, к сожалению, я не смог быстро найти тот, на который хотел бы сослаться.

1 голос
/ 30 марта 2020

loop.create_task(inner()) немедленно ставит в очередь все 5 inner задач для выполнения, а не await task. await task приостанавливает outer до тех пор, пока первое задание не будет полностью выполнено, то есть не менее 0,5 секунд. При этом все inner задачи были запущены до await, включая их print.


async def outer(loop):
    tasks = []
    for i in range(0, 5):
        # queues task immediately
        tasks.append(loop.create_task(inner()))

    for task in tasks:
        print("outer")
        # suspends for at least 0.5 seconds
        await task


async def inner():
    # runs immediately
    print("inner")
    await asyncio.sleep(0.5)
...