I'm trying to run several functions at the same time (approximately or course) with different parameters and repeat that at the start of every minute.
I managed to get an asyncio
example to run where I get a function callback
to run at specific times with a different parameter, but what I can't figure out is how to run it (and keep running it forever) at very specific times (i.e. I want to run it at the start of every minute, so at 19:00:00, 19:01:00, etc..).
Asyncio call_at
should be able to do that, but it uses a time format that is not the standard python time format and I can't figure out to specify that time format as the 00 seconds of the next minute.
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()
As others have noted, there is no built-in functionality for that kind of thing, you will need to write it yourself. It is straightforward to implement, though - a simple version could look like this:
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()
This doesn't use call_later
, it is a coroutine which can be canceled when no longer necessary. It simply takes the seconds value of the current wallclock time x
, and sleeps (60 - x)
to reach the next minute. Here is a test:
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
...
Unfortunately, the simple implementation can misbehave if asyncio.sleep
ever happens to sleep a tiny bit shorter than the requested period, e.g. due to clock skew. In that case the subsequent asyncio.sleep
will try to again reach the start of the same minute and sleep for only a fraction of a second, resulting in the callback effectively firing twice in quick succession. To prevent that, additional code is needed to compensate for the short sleeps:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With