При использовании asyncio, как разрешить все запущенные задачи для завершения перед завершением цикла событий

у меня есть следующий код:

@asyncio.coroutine
def do_something_periodically():
    while True:
        asyncio.async(my_expensive_operation())
        yield from asyncio.sleep(my_interval)
        if shutdown_flag_is_set:
            print("Shutting down")
            break

Я запускаю эту функцию до полного. Проблема возникает, когда выключение установлено - функция завершается, и любые отложенные задачи никогда не выполняются. (Вы видите это как ошибку

task: <Task pending coro=<report() running at script.py:33> wait_for=<Future pending cb=[Task._wakeup()]>>

). Как правильно запланировать завершение работы?

чтобы дать некоторый контекст, я пишу Системный монитор, который читает из /proc/stat каждые 5 секунд, вычисляет использование ЦП в этот период, а затем отправляет результат на сервер. Я хочу чтобы продолжать планировать эти задания мониторинга, пока я не получу sigterm, когда я прекращу планирование, дождитесь завершения всех текущих заданий и изящно завершите.

2 ответов


вы можете получить незавершенные задачи и запустить цикл снова, пока они не закончены, а затем закрыть цикл или выйти из программы.

pending = asyncio.Task.all_tasks()
loop.run_until_complete(asyncio.gather(*pending))
  • ожидание-это список нерешенных задач.
  • ввода-вывода.gather() позволяет одновременно выполнять несколько задач.

Если вы хотите убедиться, что все задачи выполнены внутри корутины (возможно, у вас есть" основная " корутина), вы можете сделать это таким образом, например:

@asyncio.coroutine
def do_something_periodically():
    while True:
        asyncio.async(my_expensive_operation())
        yield from asyncio.sleep(my_interval)
        if shutdown_flag_is_set:
            print("Shutting down")
            break

    yield from asyncio.gather(*asyncio.Task.all_tasks())

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

@asyncio.coroutine
def do_something_periodically():
    tasks = []
    while True:
        tasks.append(asyncio.async(my_expensive_operation()))
        yield from asyncio.sleep(my_interval)
        if shutdown_flag_is_set:
            print("Shutting down")
            break

    yield from asyncio.gather(*tasks)

начиная с Python 3.7 В приведенном выше ответе используется несколько устаревшие API (ввода-вывода.async и задач.all_tasks,@ввода-вывода.сопрограмма, доходность и т. д.) и вы должны использовать это:

import asyncio

my_interval = 1

shutdown_flag_is_set = False


async def my_expensive_operation():
    print(await asyncio.sleep(10, result="Expensive operation finished."))


async def do_something_periodically(*, loop):
    global shutdown_flag_is_set
    while True:
        loop.create_task(my_expensive_operation())
        await asyncio.sleep(my_interval)
        if shutdown_flag_is_set:
            print("Shutting down")
            break

    await  asyncio.gather(*asyncio.all_tasks(loop))


loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(do_something_periodically(loop=loop))
except KeyboardInterrupt:
    shutdown_flag_is_set = True