Python асинхронные функции create_task и await - PullRequest
0 голосов
/ 10 октября 2018

Я пытаюсь понять модуль Python Asynico и наткнулся на следующий фрагмент кода на https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task

import time
import asyncio

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print('started at', time.strftime('%X'))

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print('finished at', time.strftime('%X'))

asyncio.run(main())

Оказывается, await task2, (или task1, но не оба)может быть просто удален, и код, кажется, делает то же самое.Я нахожу это очень нелогичным, что здесь происходит?Спасибо за ваше время.

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

Существует три различных сценария, которые вы излагали:

  1. Нет await утверждений (оба комментария закомментированы)
  2. Используйте только await task1 (закомментируйте второй)
  3. Используйте только await task2 (закомментируйте первый)

Вот ваш сценарий;немного увеличьте время ожидания на task2 только для иллюстрации.

# tasktest.py
import time
import asyncio

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(3, 'world'))

    print('started at', time.strftime('%X'))
    await task1
    # await task2
    print('finished at', time.strftime('%X'))

asyncio.run(main())

1.Нет await операторов

Вот мясо asyncio.run():

loop = events.new_event_loop()
try:
    events.set_event_loop(loop)
    loop.set_debug(debug)
    return loop.run_until_complete(main)   # < -----
finally:
    try:
        _cancel_all_tasks(loop)            # < -----
        loop.run_until_complete(loop.shutdown_asyncgens())
    finally:
        events.set_event_loop(None)
        loop.close()

Важно, что цикл только заботится о том, чтобы main()завершено, а затем отменяет все другие задачи, связанные с запущенным циклом событий.(Каждая задача связана с циклом событий, когда она указана.)

Если вы определяете main() без каких-либо операторов await, create_task() планирует выполнение задач,но main() не ждет завершения одного из них.

2.await task1

Настройка:

await task1
# await task2

Вывод:

(base_py37) $ python3 tasktest.py 
started at 11:06:46
hello
finished at 11:06:47

Обе задачи переходят из режима ожидания в режим ожидания, но только task1 завершается, поскольку main()ожидается только для задачи, которая занимает ~ 1 секунду, что недостаточно для запуска task2. * (обратите внимание, что main() занимает всего 1 секунду.)

3.await task2

Настройка:

# await task1
await task2

Вывод:

(base_py37) $ python3 tasktest.py 
started at 11:08:37
hello
world
finished at 11:08:40

Обе задачи переходят из режима ожидания в режим ожидания, и теперь оба task1 и task2 завершены, потому что main() ожидается для задачи, которая занимает ~ 3 секунды, достаточно долго для выполнения обеих задач до завершения.


* Это относится, по крайней мере, к моей настройке (Mac OSX,....) но, как уже упоминалось в другом ответе, время может отличаться по времени при другой настройке, и, если время выполнения задачи схоже, оба могут запускаться в таких местах, как случай № 2.

0 голосов
/ 10 октября 2018

await не запускает сопрограммы в заданиях, оно просто говорит сопрограмме main to дождаться этих двух coros.Цикл обработки событий неявно запускается с run_until_complete, что, в свою очередь, означает, что он будет ждать завершения передаваемого ему коро (main).Именно этот coro продлевает свое собственное время жизни (на await ing) достаточно долго, чтобы гарантировать выполнение задач, созданных из него.

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))
    task2 = asyncio.create_task(
        say_after(2, 'world'))
    #await task1
    #await task2
    print(asyncio.all_tasks(asyncio.get_event_loop()))

# will print (added line breaks and shortened paths for readibility):
{
    <Task pending coro=<main() running at C:/Users/.../lmain.py:17> cb=[_run_until_complete_cb() at C:...\lib\asyncio\base_events.py:150]>, 
    <Task pending coro=<say_after() running at C:/Users/.../lmain.py:5>>, 
    <Task pending coro=<say_after() running at C:/Users/.../lmain.py:5>>
}

Как видите, все три coros работаютбез await ничего.Просто два say_after coros займут больше времени, чем main, который неявно контролирует продолжительность цикла обработки событий.

Если вы заставили main подождать, пока работа будет выполнена в цикле достаточно долго, обазадачи будут выполнены:

async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))
    task2 = asyncio.create_task(
        say_after(2, 'world'))
    print('started at', time.strftime('%X'))
    #await task1
    #await task2
    await asyncio.sleep(5)
    print('finished at', time.strftime('%X'))

# output
started at 15:31:48
hello
world
finished at 15:31:53

Итак, какая задача, если таковая имеется, завершается, когда вы тестируете комментирование task1 значения *1021* и / или task2 выше, в основном, просто вопрос времени, в основном под влиянием HW, ОС и, возможно, времени выполнения (т.е. IDE или оболочка).

PS Задачи имеют только три состояния : pending, cancelled и finished.Каждая задача находится в состоянии pending сразу после ее создания и остается в этом состоянии до тех пор, пока завернутая в нее сопрограмма не завершится (каким-либо образом) или не будет отменена контролирующим ее циклом событий.

...