Skip to content Skip to sidebar Skip to footer

An Accurate Python Sleep Function

I've tried time.sleep(), but its accuracy is total garbage. Consider this loop, for instance: for i in range(10000000): print(i) sleep(.334) Watch the numbers it prints. I

Solution 1:

If you're just looking at the output, buffering might make it appear slightly jittery. You could try to explicitly flush the output, but then you're also at the mercy of whatever is displaying the output. I might even hazard a guess that you're using Jupyter Notebook in your browser, which will also have a bunch of buffering/latency as it updates.

Another issue is that if you expect to be running every 1/3 of a second, is that you will suffer from accumulated errors. It will take a little time to run the loop, print a value (printing will take orders of magnitude more time than the other parts), then start to sleep again. A way to bypass this would be that after you finish doing whatever you want to do (I assume something more interesting than count), compute the time until the next 1/3rd of a second and sleep for that amount of time. Something like:

import random
import time

sleep_until = time.monotonic() + 1/3

for n in range(100):
    print(time.monotonic() % 100, n)
    time.sleep(random.random() / 4) # some "work"

    now = time.monotonic()
    if sleep_until > now:
        time.sleep(sleep_until - now)
    else:
        pass
        #print('task took too long')
    sleep_until += 1/3

For me it gives something like:

48.34696656104643 0
48.68041984003503 1
49.08346292399801 2
49.41925806296058 3
49.72542790300213 4
50.07280854298733 5
50.41882419097237 6
50.74827564903535 7
51.08352101803757 8
51.41813271504361 9
51.75208444998134 10
52.08399672002997 11
52.41870043799281 12

So it bounces around a bit (I'm also running this in Jupyter, which may contribute), but won't stack up error as it runs.

The real question though is what are you trying to do?


Solution 2:

The print statement in Python takes time to run. If I need a Python program to sleep for a more exact amount of time, I use datetime.timedelta with a while loop instead of time.sleep. Here is my code:

from datetime import datetime, timedelta

startTime = datetime.now()
sleepTime = timedelta(seconds = 5)

# run code here

while startTime+sleepTime > datetime.now():
    pass

Also, if you don't mind putting all of your code in funtions you can use a decorator function:

from datetime import datetime, timedelta

def sleep(time):
    def outer_wrap(func):
        def wrap(*args, **kwargs):
            startTime = datetime.now()
            sleepTime = timedelta(seconds = time)
            while startTime+sleepTime > datetime.now():
                pass
            func(*args, **kwargs)
        return wrap
    return outer_wrap

@sleep(5)
def c():
    # code goes here
    pass

c()

I would reccomend the first method because it can be very inefficient to put all your code in functions.

Note: This will not work if the code you are executing taes longer that the time you are trying to wait. It should work in your case because print shouldn't take longer than 0.334 seconds.


Solution 3:

OK, this time I have really figured this out. It wasn't IO buffering, nor was it the time it takes to call print() or some spooky OS-specific scheduling voodoo. It was my terminal all along. (...m. night shyamalan twist...) Specifically, xfce4-terminal, though after trying about seven other terminals, I found lxterminal has the exact same problem. They're both VTE-based, but there are other VTE-based emulators that don't have the problem, like terminator and gnome-terminal. Anyway, I changed terminals, and the problem is fixed.

I bet those of you who weren't using either of the two problematic terminals were wondering what the hell I was talking about.


Post a Comment for "An Accurate Python Sleep Function"