10

I want to write a function which will return after 5 seconds no matter what:

def myfunction():
    while passed_time < 5_seconds:
        do1()
        do2()
        do3()
        .
        .
    return

I mean, this function run for 5 seconds only, after 5 seconds, it should end, and continue with other function:

myfunction()
otherfunction()   ----> This should start 5 seconds after myfunction() is executed.

Best Regards

Dr. Jan-Philip Gehrcke
  • 33,287
  • 14
  • 85
  • 130
alwbtc
  • 28,057
  • 62
  • 134
  • 188
  • Is there any chance that the execution of any of your functions lasts longer than 5 seconds? – Dr. Jan-Philip Gehrcke Aug 31 '12 at 14:10
  • What if half way through function `do2`, 5 seconds has past? – Eric Aug 31 '12 at 14:11
  • Yes, if I leave it on its own, myfunction() may last for hours. But, I want it to end after 5 seconds, and the code should continue with the next function. – alwbtc Aug 31 '12 at 14:11
  • In that case you need to run the two functions on different `threads` and start the second one 5 seconds after the first one. – Ansgar Wiechers Aug 31 '12 at 14:12
  • 3
    possible duplicate of [Timeout on a Python function call](http://stackoverflow.com/questions/492519/timeout-on-a-python-function-call) – mgilson Aug 31 '12 at 14:12
  • If 5 seconds passes, myfunction() should return after the last statement is executed in myfunction(). – alwbtc Aug 31 '12 at 14:12
  • @alwbtc: That's fairly doable. See my answer – Eric Aug 31 '12 at 14:14
  • @alwbtc I think we are still unsure of what exactly that you want. Are you saying you want to do as much as possible of `myfunction` in 5 seconds and then kill it? Do you want it to keep running in the background? It seems like you are using a round-about way to solve another problem. Could give more context to your code? – Alexander Kondratskiy Aug 31 '12 at 14:17
  • No I dont want it to run in background. `myfunction()` may last for 20 hours. But, for example, 3 hours of it is sufficient for me. When 3 hours passes since `myfunction()` starts, it should end, and the program should continue with the next function: `otherfunction()` – alwbtc Aug 31 '12 at 14:22
  • Do you actually want to loop the contents of `myfunction`? (Assuming the calls ran in negligable time) – Eric Aug 31 '12 at 14:24
  • No, there are no loops in myfunction(). But it lasts too long. To be more specific, myfunction() is an optimizer, and optimization lasts too long. 3 hours of optimization is sufficient for me, because it will not yield a siginificantly better result after 3 hours. Therefore I want to cutoff after 3 hours. So that the next optimizer otherfunction() will begin. This way the whole optimization process will be completed in shorter time. – alwbtc Aug 31 '12 at 14:31

4 Answers4

6

You can do:

def myfunction():
    start = time.time()
    while time.time() < start + 5:
        do1()
        do2()
        do3()

Note that this will stop after at least 5 seconds - if do1, do2, and do3 each take 3 seconds, then this function will take 9 seconds.


If you want to cut off myFunction between these calls, you can do:

def myfunction():
    todo = itertools.cycle([do1, do2, do3])
    start = time.time()
    while time.time() < start + 5:
        todo.next()()

This case would take 6s

Eric
  • 95,302
  • 53
  • 242
  • 374
3

If there is a chance that the execution time of your functions is longer than the interval between firing these functions, you'd have to start these functions in another thread or process.

Example with threading:

import time
import sys
from threading import Thread


def f1():
    print "yo!!"
    time.sleep(6)


def f2():
    print "boring."
    time.sleep(2)


if __name__ == "__main__":
    threads = []
    try:
        for f in [f1, f2]:
            t = Thread(target=f)
            t.start()
            threads.append(t)
            time.sleep(5)
    except KeyboardInterrupt:
        sys.exit(0)
    finally:
        [t.join() for t in threads]

I understood the question in the way that you do not necessarily need to kill/end one function after exactly 5 seconds. Your main goal is to fire up the functions with a 5 second interval. If you need to 'kill' the functions after a certain time, this won't be trivial using threads: Is there any way to kill a Thread in Python?

Community
  • 1
  • 1
Dr. Jan-Philip Gehrcke
  • 33,287
  • 14
  • 85
  • 130
  • 1
    Thanks. I will give it a try. How do you know all these things? I don't even know a module named threading exists. Do you study all modules? – alwbtc Aug 31 '12 at 14:25
  • 1
    Yes, we study all the modules that we need. These things take time and the experience grows over the months/years. Ontopic: I now realize that you start talking about hours instead of seconds and that being able to 'kill' a 'function' is now a requirement. If it is about hours and heavy tasks, you should consider spawning subprocesses using the multiprocessing module. They have a `terminate` method sending a signal to the subprocess. You can handle this singal in the subprocess and act correspondingly. – Dr. Jan-Philip Gehrcke Aug 31 '12 at 14:29
1

You can also do:

import time 
start=time.time()   

stuff={1:do1,2:do2,3:do3,...}
i=1

while time.time() <= start+5:
    stuff[i]()
    i+=1
user1544624
  • 887
  • 1
  • 10
  • 15
1

From the way you've defined your problem, I think this is a good situation for using a generator based co-routine. That is, change myFunction to a generator that yields every time it has finished with a step of its processing (e.g. do1 or do2) and run it from a loop that checks the time.

Here's a simple implementation that repeatedly runs a single somework function, but you could easily call different functions, or even do work directly in the myFunction generator:

import time

def somework():
    print "Doing some work!"
    time.sleep(2)

def myFunction():
    try:
        print "Started myFunction()"
        yield somework()
        yield somework()
        yield somework()
        yield somework()
        yield somework()
        print "Done with myFunction()"
    except GeneratorExit:
        print "Exiting myFunction() early!"

def main():
    start = time.time()
    gen = myFunction()
    for _ in gen:
        if time.time() > start+5:
            gen.close();

There are some benefits to using this structure:

  1. First off, myFunction gets to choose where it yields to the outer loop, so it won't get killed at an unexpected time. If it's manipulating some external data structure, you can ensure it always yields with everything in a consistent state.
  2. Second, myFunction can yield intermediate values, if that is useful. The example code above yields None (since that's the return value from somework but you can change that easily. The main loop puts the latest yielded value in the _ variable (which is not used), but you can rename that variable and use its value in the timeout block if you want.
  3. Finally, by catching the GeneratorExit exception, myFunction can run any cleanup code that it needs before it shuts down.
Blckknght
  • 100,903
  • 11
  • 120
  • 169