asyncio - 多次等待协程(周期性任务)

10

我正在尝试为asyncio事件循环创建一个周期性任务,如下所示。但是,我遇到了"RuntimeError: cannot reuse already awaited coroutine" 异常。显然,asyncio不允许等待相同的可等待函数,正如这个错误线程中所讨论的那样。以下是我的实现方式:

import asyncio    

class AsyncEventLoop:    

    def __init__(self):
        self._loop = asyncio.get_event_loop()

    def add_periodic_task(self, async_func, interval):
        async def wrapper(_async_func, _interval):
            while True:
                await _async_func               # This is where it goes wrong
                await asyncio.sleep(_interval)
        self._loop.create_task(wrapper(async_func, interval))
        return

    def start(self):
        self._loop.run_forever()
        return

由于我的while循环,相同的可等待函数(_async_func)将会在两次执行之间有一个休眠间隔。我从如何使用asyncio定期执行函数中获取了实现周期性任务的灵感。

从上面提到的错误线程中,我推断出RuntimeError背后的想法是为了防止开发人员意外地重复等待同一个协程,因为该协程将被标记为完成并产生None而不是结果。是否有办法可以多次等待同一个函数?


3
为了加剧混乱,链接的错误描述中有一个糟糕的拼写错误,使它看起来像是建议禁止连续两次实例化和等待同一个协程函数![随后的评论](https://bugs.python.org/msg256567)澄清了混淆,明确指出不允许等待已经等待过的协程*对象*。 - user4815162342
1
有了这个前提,考虑一下如果恢复旧的行为并删除RuntimeError会发生什么。周期性协程不会神奇地重置并从头开始(Python中没有自动实现这种机制,也不需要,因为可以使用循环来实现相同的效果)。相反,下一个await将产生一个虚假的None值,这不是任何人所期望或想要的。 - user4815162342
1个回答

8

看起来你把异步函数(协程函数)和协程(这些异步函数生成的值)混淆了。

考虑以下异步函数:

async def sample():
    await asyncio.sleep(3.14)

你正在传递其调用结果:add_periodic_task(sample(), 5)
相反,你应该传递异步函数本身:add_periodic_task(sample, 5),然后在你的包装器中调用它。
while True:
    await _async_func()
    await asyncio.sleep(_interval)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接