`asyncio.подождите` замешательство при прохождении сопрограммы

#python #async-await #task #python-asyncio #coroutine

Вопрос:

асинсио,подожди, говорит:

Передача объектов сопрограмм напрямую в wait() не рекомендуется, так как это приводит к запутанному поведению.

и выдает это за сбивающее с толку поведение:

wait() автоматически планирует сопрограммы как задачи, а затем возвращает эти неявно созданные объекты задач в наборах (готово, ожидание). Поэтому следующий код не будет работать должным образом: [Я изменил приведенный ниже фрагмент кода, чтобы он работал]

 async def foo():
    return 42

async def main():
    coro = foo()
    done, pending = await asyncio.wait({coro})

    if coro in done:
        # This branch will never be run!
        print('yay!')

asyncio.run(main())
 

Я только начал узнавать об asyncio этом , поэтому не совсем понимаю. Может кто-нибудь объяснить?

Комментарии:

1. asyncio.wait не обязательно вызывается в await заявлении, и это не является частью путаницы, которая здесь подразумевается.

Ответ №1:

Пример кода, приведенный в документации, является:

 coro = foo()
done, pending = await asyncio.wait({coro})

if coro in done:
    # This branch will never be run!
 

Причина, по которой этот код дает неожиданные результаты, заключается в следующем:

  • coro является объектом сопрограммы.
  • Когда он передается asyncio.wait , он автоматически создает Task из него объект (который отличается от объекта сопрограммы), например coro_task = create_task(coro) (см. create_task ).
  • Когда asyncio.wait это будет сделано, он вернет два набора:
    • набор задач, которые уже выполнены
    • набор задач, которые еще не выполнены

Таким образом, в этом случае он вернет один набор, который содержит coro_task , и один набор, который пуст.

Обратите внимание, что исходный объект сопрограммы coro (который отличается от coro_task ) не содержится ни в одном из наборов, поэтому проверять, находится ли он в наборе «готово», бессмысленно — он никогда не будет содержаться, даже если соответствующая задача coro_task уже выполнена.

Исправление заключается в создании Task объекта для coro внешнего asyncio.wait , что позволит проверить, содержится ли тот же объект в одном или другом из возвращаемых наборов, чтобы определить, выполнена ли задача.

В версиях Python , начиная с 3.8, вы получите предупреждение об устаревании, если передадите сопрограмму asyncio.wait , и начиная с версии 3.11 это будет ошибка, т. е. вы вынуждены использовать «исправленный» код.