DNN
DNN

Reputation: 871

Run a python function every second

What I want is to be able to run a function every second, irrelevant of how long the function takes (it should always be under a second). I've considered a number of options but not sure which is best.

If I just use the delay function it isn't going to take into account the time the function takes to run.

If I time the function and then subtract that from a second and make up the rest in the delay it's not going to take into account the time calculations.

I tried using threading.timer (I'm not sure about the ins and outs of how this works) but it did seem to be slower than the 1s.

Here's the code I tried for testing threading.timer:

def update(i):
    sys.stdout.write(str(i)+'\r')
    sys.stdout.flush()
    print i
    i += 1
    threading.Timer(1, update, [i]).start()

Is there a way to do this irrelevant of the length of the time the function takes?

Upvotes: 5

Views: 38623

Answers (7)

Abdul Saboor
Abdul Saboor

Reputation: 11

If you are working in Jupyter Notebook, you can make use of ipywidgets.Play widget's observe method to run a function after certain interval without blocking and keeping code in the main thread.

Upvotes: 0

jfs
jfs

Reputation: 414089

if f() always takes less than a second then to run it on a one second boundary (without a drift):

import time

while True:
    time.sleep(1 - time.monotonic() % 1)      
    f()

The idea is from @Dave Rove's answer to a similar question.

To understand how it works, consider an example:

  1. time.monotonic() returns 13.7 and time.sleep(0.3) is called
  2. f() is called around (±some error) 14 seconds (since time.monotonic() epoch)
  3. f() is run and it takes 0.1 (< 1) seconds
  4. time.monotonic() returns around 14.1 seconds and time.sleep(0.9) is called
  5. Step 2. is repeated around 15 seconds (since time.monotonic() epoch)
  6. f() is run and it takes 0.3 (< 1) seconds (note: the value is different from Step 2.)
  7. time.monotonic() returns around 15.3 and time.sleep(0.7) is called
  8. f() is called around 16 seconds and the loop is repeated.

At each step f() is called on a one second boundary (according to time.monotonic() timer). The errors do not accumulate. There is no drift.

See also: How to run a function periodically in python (using tkinter).

Upvotes: 6

Grasshopper
Grasshopper

Reputation: 436

I would like to recommend the following code. You can replace the True with any condition if you want.

while True:
time.sleep(1) #sleep for 1 second
func()    #function you want to trigger

Tell me if it works.

Upvotes: -1

moooeeeep
moooeeeep

Reputation: 32502

The approach using a threading.Timer (see code below) should in fact not be used, as a new thread is launched at every interval and this loop can never be stopped cleanly.

# as seen here: https://stackoverflow.com/a/3393759/1025391
def update(i):
  threading.Timer(1, update, [i+1]).start()
  # business logic here

If you want a background loop it is better to launch a new thread that runs a loop as described in the other answer. Which is able to receive a stop signal, s.t. you can join() the thread eventually.

This related answer seems to be a great starting point to implement this.

Upvotes: 7

Znatz
Znatz

Reputation: 1530

Threading may be a good choice. The basic concept is as follows.

import threading

def looper():    
    # i as interval in seconds    
    threading.Timer(i, looper).start()    
    # put your action here
    foo()

#to start 
looper()

Upvotes: 1

Rodrigo Queiro
Rodrigo Queiro

Reputation: 1336

This will do it, and its accuracy won't drift with time.

import time

start_time = time.time()
interval = 1
for i in range(20):
    time.sleep(start_time + i*interval - time.time())
    f()

Upvotes: 10

lllluuukke
lllluuukke

Reputation: 1342

How about this: After each run, sleep for (1.0 - launch interval) seconds. You can change the terminate condition by changing while True:. Although if the your function takes more than 1 second to run, this will go wrong.

from time import time, sleep

while True:
    startTime = time()
    yourFunction()
    endTime = time()-startTime
    sleep(1.0-endTime)

Upvotes: 1

Related Questions