如何使用 asyncio 定期执行函数?

新手上路,请多包涵

I’m migrating from tornado to asyncio , and I can’t find the asyncio equivalent of tornado ’s PeriodicCallback 。 (A PeriodicCallback 有两个参数:要运行的函数和调用之间的毫秒数。)

  • asyncio 中是否有这样的等价物?
  • 如果没有,什么是最干净的实现方法,而不会冒一段时间后得到 RecursionError 的风险?

原文由 2Cubed 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 701
2 个回答

对于 3.5 以下的 Python 版本:

 import asyncio

@asyncio.coroutine
def periodic():
    while True:
        print('periodic')
        yield from asyncio.sleep(1)

def stop():
    task.cancel()

loop = asyncio.get_event_loop()
loop.call_later(5, stop)
task = loop.create_task(periodic())

try:
    loop.run_until_complete(task)
except asyncio.CancelledError:
    pass

对于 Python 3.5 及更高版本:

 import asyncio

async def periodic():
    while True:
        print('periodic')
        await asyncio.sleep(1)

def stop():
    task.cancel()

loop = asyncio.get_event_loop()
loop.call_later(5, stop)
task = loop.create_task(periodic())

try:
    loop.run_until_complete(task)
except asyncio.CancelledError:
    pass

原文由 A. Jesse Jiryu Davis 发布,翻译遵循 CC BY-SA 4.0 许可协议

当您觉得应该在您的 asyncio 程序的“后台”发生某些事情时, asyncio.Task 可能是实现它的好方法。您可以阅读 这篇文章 以了解如何处理任务。

这是定期执行某些功能的类的可能实现:

 import asyncio
from contextlib import suppress

class Periodic:
    def __init__(self, func, time):
        self.func = func
        self.time = time
        self.is_started = False
        self._task = None

    async def start(self):
        if not self.is_started:
            self.is_started = True
            # Start task to call func periodically:
            self._task = asyncio.ensure_future(self._run())

    async def stop(self):
        if self.is_started:
            self.is_started = False
            # Stop task and await it stopped:
            self._task.cancel()
            with suppress(asyncio.CancelledError):
                await self._task

    async def _run(self):
        while True:
            await asyncio.sleep(self.time)
            self.func()

让我们测试一下:

 async def main():
    p = Periodic(lambda: print('test'), 1)
    try:
        print('Start')
        await p.start()
        await asyncio.sleep(3.1)

        print('Stop')
        await p.stop()
        await asyncio.sleep(3.1)

        print('Start')
        await p.start()
        await asyncio.sleep(3.1)
    finally:
        await p.stop()  # we should stop task finally

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

输出:

 Start
test
test
test

Stop

Start
test
test
test

[Finished in 9.5s]

正如您在 start 中看到的那样,我们只是开始调用一些函数并在无限循环中休眠一段时间的任务。在 stop 我们只是取消了那个任务。请注意,该任务应在程序完成时停止。

一件更重要的事情是你的回调不应该花太多时间来执行(否则它会冻结你的事件循环)。如果您打算调用一些长时间运行的 func ,您可能需要 在 executor 中运行它

原文由 Mikhail Gerasimov 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题