2

I want to call a function (actually, it is a web API) every 10 seconds. But the function may take random t second to return. (Assume t is 0.1 to 1.0 sec)

The simplest code we can think of is

while True:
    func()          # take t sec
    time.sleep(10)  # sleep 10 sec

but in this case, func is called every (1+t) seconds. Are there better ways to do it?

Should we use some multi threading or something like that? Concrete code example will be appreciated. Thank you.

ywat
  • 2,757
  • 5
  • 24
  • 32

4 Answers4

3

Just remember when the next iteration is supposed to happen, roughly like this (not fully working code, I'm sure you can figure it out from here):

import time
nexttime = time.time()
while True:
    func()          # take t sec
    nexttime += 10
    sleeptime = nexttime - time.time()
    if sleeptime > 0:
        time.sleep(sleeptime)
ywat
  • 2,757
  • 5
  • 24
  • 32
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
1

Try this:

from multiprocessing import Pool
import time

pool = Pool(processes=1)              # Start a worker processes.
while true:
    result = pool.apply_async(func, [10], callback=finish) # Evaluate "func(10)" asynchronously calling callback when finished.
    time.sleep(10)

This will call your function exactly every 10 seconds. As long as you are sure it will not take more than 10 seconds to return you should be ok with only one worker process. Sourced from here: Asynchronous method call in Python?

sakurashinken
  • 3,940
  • 8
  • 34
  • 67
  • Thank you for the nice code. This is what I vaguely imagined of. I found that the interval is slightly longer than 10 sec, due to `pool.apply_async` invoking time. – ywat May 28 '17 at 03:26
1

I found another approach using sched module.

import time
import sched

def daemon(local_handler, t):
    print('time {}'.format(t))
    # call func here
    # time.sleep(2)
    local_handler.enterabs(t + 10, 1, daemon, (local_handler, t+10))

handler = sched.scheduler(time.time, time.sleep)
t = time.time()
handler.enter(0, 1, daemon, (handler, t))
handler.run()

A good point of this approach is that we can control the time of the function exactly. For example we can call the function when the clock is 0.31, 10.31, 20.31, 30.31, ...

This code is taken from Python Scheduler vs loop + sleep.

ywat
  • 2,757
  • 5
  • 24
  • 32
0

You can use something like this:

import time
start=time.perf_counter()
call_new_func()
while ((time.perf_counter()-start)<desired_time_wait):
    pass #busy-wait

Inside your while True loop.

rlee827
  • 1,853
  • 2
  • 13
  • 32
  • Sure, if you want to waste electricity and slow down your computer! Note OP's code has a duty cycle of less than 10%, so this will waste more than 90% of resources. – John Zwinck May 28 '17 at 02:18
  • According to my understanding of https://hg.python.org/cpython/file/tip/Modules/timemodule.c, this kind of loop (in C) is actually what Python does internally for `time.sleep()`. – rlee827 May 28 '17 at 02:22
  • No, Python uses `select()` or other non-busy-waiting approaches. As you can see in the code for `pysleep` you linked. – John Zwinck May 28 '17 at 02:27