我尝试使用不同的参数同时(大约或过程)运行多个功能,并在每分钟开始时重复这些功能。
我设法让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()
答案 0 :(得分: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
答案 1 :(得分:1)
没有简单的方法可以做到这一点。你不能依赖loop.time()
作为“真实世界”的时钟。
唯一的方法是使用datetime
/ time
模块计算timedelta,并使用计算的延迟调用loop.call_later
。是的,它超级麻烦。
请查看此问题以获取示例:How can I periodically execute a function with asyncio?
答案 2 :(得分:1)
就像一些评论员所说的那样,在纯Python中只有asyncIO这样的弹性方法并不容易,但是使用 apscheduler 库它实际上非常容易。
from apscheduler.schedulers.asyncio import AsyncIOScheduler
import asyncio
import os
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