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

Posted

技术标签:

【中文标题】asyncio - 多次等待协程(周期性任务)【英文标题】:asyncio - await coroutine more than once (periodic tasks) 【发布时间】:2018-12-09 12:58:58 【问题描述】:

我正在尝试为 asyncio 事件循环创建一个定期任务,如下所示,但是我收到“RuntimeError:无法重用已等待的协程”异常。显然,asyncio 不允许像 this bug thread 中讨论的那样等待相同的可等待函数。这就是我尝试实现它的方式:

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) 将在其间有一个睡眠间隔执行。执行周期性任务的灵感来自How can I periodically execute a function with asyncio? 。

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

【问题讨论】:

为了加剧混乱,链接错误的描述有一个可怕的错字,看起来它建议禁止实例化并连续两次等待同一个协程函数! [后续 cmets] (bugs.python.org/msg256567) 消除了混乱,明确表示不允许等待已经等待的协程 object 除此之外,考虑一下如果恢复旧行为并删除 RuntimeError 会发生什么。周期性协程不会被神奇地重置并从一开始就启动(Python 中没有自动实现这一点的机制,也不需要,因为可以使用循环来实现同样的事情)。相反,下一个 await 会产生一个虚假的 None 值,这不是任何人所期望或想要的。 【参考方案1】:

您似乎将异步函数(协程函数)与协程(这些异步函数产生的值)混淆了。

考虑这个异步函数:

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)

【讨论】:

以上是关于asyncio - 多次等待协程(周期性任务)的主要内容,如果未能解决你的问题,请参考以下文章

asyncio 异步协程

python协程(4):asyncio

高性能编程之协程--------asyncio

一初识asyncio协程

带有nest_asyncio包的asyncio的异步/等待总是返回协程'get1'从未等待

asyncio:Python异步编程模块