问题
I want to have a loop be executed once every minute when datetime.utcnow().second
is zero.
So far I have this
while True:
while datetime.utcnow().second != 0: pass
do_something()
But the problem with this is that I am wasting cpu processes. I would use time.sleep(60)
, but I don't know how it would sync with the UTC clock, because time.sleep(60)
could stray from the official UTC time as time passes.
回答1:
Best way I can think of would be to sleep until the next minute:
while True:
sleeptime = 60 - datetime.utcnow().second
time.sleep(sleeptime)
...
If you want to be really precise:
while True:
t = datetime.utcnow()
sleeptime = 60 - (t.second + t.microsecond/1000000.0)
time.sleep(sleeptime)
...
This sleeps for exactly the amount of time necessary to reach the next minute, with subsecond precision.
EDITED to fix minute rollover bug.
回答2:
Interesting problem python does have a way to schedule events using sched and to make it reoccurring you can just schedule it again and again and so on ...
Here how it would look like.
>>> import sched, time, datetime
>>> sch = sched.scheduler(time.time, time.sleep)
>>>
>>> count = 0
>>> last = 0
>>> def do_something():
... global count, last
... if count != 5:
... sch.enter(5, 1, do_something, ())
... print datetime.datetime.now() - last
... last = datetime.datetime.now()
... count += 1
...
>>> sch.enter(5, 1, do_something, ())
Event(time=1345872167.9454501, priority=1, action=<function do_something at 0x1004c4a28>, argument=())
>>> last = datetime.datetime.now()
>>> sch.run()
0:00:05.000015
0:00:05.000159
0:00:05.000184
0:00:05.000183
0:00:05.000181
0:00:05.000148
>>>
interesting, it seems to be pretty accurate, with a slight overhead probably due to the time it takes to execute the actual instructions, I guess we need to take them into account, if we want perfect accuracy ...
回答3:
The naive approach would be too change the pass
to a sleep (1)
which would greatly ease the CPU pressure, but you run the risk of missing the zero-second if for some reason you get 12:30:59.99999
followed by 12:31:01.00001
.
You could opt for a two-stage approach. While there's more than three seconds to go, wait for a longer time based on the amount of time to go. Otherwise, wait for one second.
Something like the following fully functional Python program will illustrate what I mean:
from datetime import datetime
from time import sleep
def WaitForNextMinute():
secs = datetime.utcnow().second
while secs < 57:
sleep (57 - secs)
secs = datetime.utcnow().second
while datetime.utcnow().second >= 57:
sleep (1)
while True:
WaitForNextMinute()
print datetime.utcnow()
The output on my system is:
2012-08-25 04:16:00.111257
2012-08-25 04:17:00.157155
2012-08-25 04:18:00.217356
2012-08-25 04:19:00.270348
2012-08-25 04:20:00.330203
2012-08-25 04:21:00.390318
2012-08-25 04:22:00.450440
2012-08-25 04:23:00.510491
2012-08-25 04:24:00.570487
2012-08-25 04:25:00.630502
2012-08-25 04:26:00.690523
2012-08-25 04:27:00.750642
2012-08-25 04:28:00.810780
2012-08-25 04:29:00.870900
2012-08-25 04:30:00.931078
which is pretty much what you're after.
This function will basically do one wait that gets you very close to the end of the minute and then wake up every second until it cycles over.
So, for example, let's say it's 12:21:13
. Because secs
is less than 57
, it will wait 57 - 13
or enough to get you up to about 12:21:57
.
From there, it will simply sleep one second at a time until the second
becomes less than 57
. In other words, when we roll into the next minute.
By doing it this way rather than trying to detect the zero second, you also bypass the possibility on missing a minute.
And, if you want to ensure you get as close to the minute rollover as possible, you can replace the sleep (1)
with a pass
. That way, you'll run at full CPU grunt for at most three seconds out of sixty (averaging 5%) and get about as close to the minute rollover as possible.
When I perform that little modification, I get:
2012-08-25 05:48:00.000003
2012-08-25 05:49:00.000003
2012-08-25 05:50:00.000003
2012-08-25 05:51:00.000004
2012-08-25 05:52:00.000004
2012-08-25 05:53:00.000004
2012-08-25 05:54:00.000003
2012-08-25 05:55:00.000004
来源:https://stackoverflow.com/questions/12119031/how-to-have-loop-sync-with-utc-timer-and-execute-at-every-new-minute