如何等待Python asyncio call_later完成所有待处理的操作

时间:2018-10-24 22:06:53

标签: python timer python-asyncio

我想编写一个程序,利用call_later来发信号通知某事(或事物)稍后发生,它们本身可能会添加更多要调用的例程,然后发信号通知该程序运行,直到没有剩余要执行的操作为止。运行。例如,我可以执行以下操作:

import  asyncio
START_RATE = 1

async def say_hi(who):
    print(f'hello {who}')

async def call_later(t, func, *params):
    await asyncio.sleep(t)
    await func(*params)

def run():
    # Let's go!
    incr = START_RATE
    loop = asyncio.get_event_loop()
    tasks = []
    for x in range(5):
        wait_delta = 5 - x
        tasks.append(
            call_later(incr * wait_delta, say_hi, x)
        )

    loop.run_until_complete(asyncio.gather(*tasks))

run()

但是您会注意到call_later是定制的协程。

是否可以使用事件循环的call_later,但是以某种方式检查事件循环或以其他方式等待所有未完成的回调的完成?

1 个答案:

答案 0 :(得分:1)

您可以使用asyncio.all_tasks()进行内省,但这可能是错误的方法。

正确的方法是创建期货并允许call_later将其标记为已完成:

import asyncio
from functools import partial
START_RATE = 1


def say_hi(who):
    print(f'hello {who}')


def run_on_finish(callback):
    def wrapper(func, *args, **kwargs):
        try:
            return func(*args, **kwargs)
        finally:
            callback()
    return wrapper


def release_waiter(waiter, *args):
    """Taken from standard library"""
    if not waiter.done():
        waiter.set_result(None)


def run():
    # Let's go!
    incr = START_RATE
    loop = asyncio.get_event_loop()
    tasks = []
    for x in range(5):
        wait_delta = 5 - x

        # Create a waiter
        waiter = loop.create_future()
        release_cb = partial(release_waiter, waiter)

        # Schedule the function, making sure we release the waiter on finish
        handle = loop.call_later(incr * wait_delta, run_on_finish(release_cb),
                                 say_hi, x)

        # If waiter is cancelled, cancel the handle too.
        waiter.add_done_callback(lambda *args: handle.cancel)

        tasks.append(waiter)

    loop.run_until_complete(asyncio.gather(*tasks))


run()

请紧记call_later用于正常功能,而不是协程。如果say_hi必须是协程,则应在混合中添加ensure_futureloop.create_task

添加它们确实带来了更多的复杂性-您需要添加一些其他功能来把握ensure_future带来的未来,并以类似于futures._chain_future的方式将其链接到服务员。

强烈建议您在这种情况下使用自己的协程。