繁体   English   中英

使用 asyncio 在每分钟的开始(00 秒)运行一个函数

[英]Using asyncio to run a function at the start (00 seconds) of every minute

我正在尝试使用不同的参数同时(大约或当然)运行多个函数,并在每分钟开始时重复。

我设法得到了一个asyncio示例来运行,在那里我得到一个函数callback ,以使用不同的参数在特定时间运行,但我无法弄清楚如何在非常特定的时间运行它(并永远运行它)(即我想在每分钟开始时运行它,所以在 19:00:00、19:01:00 等等)。

Asyncio call_at应该能够做到这一点,但它使用的时间格式不是标准的 python 时间格式,我无法弄清楚将该时间格式指定为下一分钟的 00 秒。

import asyncio
import time


def callback(n, loop, msg):
    print(msg)
    print('callback {} invoked at {}'.format(n, loop.time()))


async def main(loop):
    now = loop.time()
    print('clock time: {}'.format(time.time()))
    print('loop  time: {}'.format(now))

    print('registering callbacks')
    loop.call_at(now + 0.2, callback, 1, loop, 'a')
    loop.call_at(now + 0.1, callback, 2, loop, 'b')
    loop.call_soon(callback, 3, loop, 'c')

    await asyncio.sleep(1)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()

正如其他人所指出的,这种事情没有内置功能,您需要自己编写。 不过,实现起来很简单 - 一个简单的版本可能如下所示:

import asyncio, datetime

async def at_minute_start(cb):
    while True:
        now = datetime.datetime.now()
        after_minute = now.second + now.microsecond / 1_000_000
        if after_minute:
            await asyncio.sleep(60 - after_minute)
        cb()

这不使用call_later ,它是一个可以在不再需要时取消的协程。 它只是获取当前挂钟时间x的秒值,然后休眠(60 - x)以到达下一分钟。 这是一个测试:

import time
loop = asyncio.get_event_loop()
loop.create_task(at_minute_start(lambda: print(time.asctime())))
loop.run_forever()

# output:
Wed Feb 28 21:36:00 2018
Wed Feb 28 21:37:00 2018
Wed Feb 28 21:38:00 2018
...

不幸的是,如果asyncio.sleep碰巧睡眠时间比请求的周期一点,例如由于时钟偏差,那么简单的实现可能会出错。 在这种情况下,后续的asyncio.sleep将尝试再次到达同一分钟的开始,并且只休眠几分之一秒,从而导致回调有效地连续快速触发两次。 为了防止这种情况,需要额外的代码来补偿短暂的睡眠:

async def at_minute_start(cb):
    now = datetime.datetime.now()
    wait_for_next_minute = False
    while True:
        after_minute = now.second + now.microsecond / 1_000_000
        if after_minute != 0:
            to_next_minute = 60 - after_minute
        else:
            to_next_minute = 0  # already at the minute start
        if wait_for_next_minute:
            to_next_minute += 60
        await asyncio.sleep(to_next_minute)
        cb()
        prev = now
        now = datetime.datetime.now()
        # if we're still at the same minute, our sleep was slightly
        # too short, so we'll need to wait an additional minute
        wait_for_next_minute = now.minute == prev.minute

就像一些评论员所说的那样,在纯 Python 中仅使用 asyncIO 没有简单的弹性方法来做到这一点,但是使用apscheduler库实际上变得非常容易。

import asyncio
import datetime
import os

from apscheduler.schedulers.asyncio import AsyncIOScheduler


def tick():
    print("Tick! The time is: %s" % datetime.datetime.now())


if __name__ == "__main__":
    scheduler = AsyncIOScheduler()
    scheduler.add_job(tick, "cron", minute="*")
    scheduler.start()
    print("Press Ctrl+{0} to exit".format("Break" if os.name == "nt" else "C"))

    # Execution will block here until Ctrl+C (Ctrl+Break on Windows) is pressed.
    try:
        asyncio.get_event_loop().run_forever()
    except (KeyboardInterrupt, SystemExit):
        pass

没有简单的方法可以做到这一点。 您不能依赖loop.time()作为“真实世界”时钟。

唯一的解决方法是使用datetime / time模块计算loop.call_later并使用计算的延迟调用loop.call_later 是的,它超级麻烦。

看看这个问题的例子: 如何使用 asyncio 定期执行函数?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM