【问题标题】:Using asyncio to run a function at the start (00 seconds) of every minute使用 asyncio 在每分钟的开始(00 秒)运行一个函数
【发布时间】:2018-08-08 16:16:03
【问题描述】:

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

我设法得到了一个 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()

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

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

    唯一的办法是用 datetime/time 模块计算时间增量,然后调用 loop.call_later 并计算延迟。是的,它超级麻烦。

    看看这个问题的例子:How can I periodically execute a function with asyncio?

    【讨论】:

    • 是的,我找到了一个很好的库,它使所有过程都非常简单,apscheduler,我会用它发布答案。
    【解决方案2】:

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

    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
    

    【讨论】:

      【解决方案3】:

      正如一些评论员所说,在纯 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
      

      【讨论】:

      • 是否可以每 30 秒打勾?喜欢:00:00 , 00:30 ...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多