timer interrupt thread python

2019-07-17 04:41发布

问题:

I've been trying to make a precise timer in python, or as precise a OS allows it to be. But It seams to be more complicated than I initially thought.

This is how I would like it to work:

from time import sleep
from threading import Timer

def do_this():
    print ("hello, world")


t = Timer(4, do_this)
t.start()
sleep(20)
t.cancel()

Where during 20 seconds I would execute 'do_this' every fourth second. However 'do_this' executes once then the script terminates after 20 seconds.

Another way would be to create a thred with a while loop.

import time
import threading
import datetime

shutdown_event = threading.Event()

def dowork():
    while not shutdown_event.is_set():
        print(datetime.datetime.now())
        time.sleep(1.0)

def main():

    t = threading.Thread(target=dowork, args=(), name='worker')
    t.start()

    print("Instance started")

    try:
        while t.isAlive():
            t.join(timeout=1.0)
    except (KeyboardInterrupt, SystemExit):
            shutdown_event.set()
    pass

if __name__ == '__main__':
    main()

This thread executes as expected but I get a timing drift. I in this case have to compensate for the time it takes to execute the code in the while loop by adjusting the sleep accordingly.

Is there a simple way in python to execute a timer every second (or any interval) without introducing a drift compared to the system time without having to compensate the sleep(n) parameter?

Thanks for helping,

/Anders

回答1:

If dowork() always runs in less time than your intervals, you can spawn a new thread every 4 seconds in a loop:

def dowork():
  wlen = random.random()
  sleep(wlen) # Emulate doing some work
  print 'work done in %0.2f seconds' % wlen

def main():
  while 1:
    t = threading.Thread(target=dowork)
    time.sleep(4)

If dowork() could potentially run for more than 4 seconds, then in your main loop you want to make sure the previous job is finished before spawning a new one.

However, time.sleep() can itself drift because no guarantees are made on how long the thread will actually be suspended. The correct way of doing it would be to figure out how long the job took and sleep for the remaining of the interval. I think this is how UI and game rendering engines work, where they have to display fixed number of frames per second at fixed times and rendering each frame could take different length of time to complete.