110

how would I stop a while loop after 5 minutes if it does not achieve what I want it to achieve.

while true:
    test = 0
    if test == 5:
        break
    test = test - 1

This code throws me in an endless loop.

2
  • 4
    you end up in an endless loop, because in the last line must be: test = test +1 May 9, 2019 at 11:32
  • 3
    that's funny that the code itself doesn't make any sense (with unreachable if condition), but everyone other than @MichaelKüller did not notice it.
    – techkuz
    Nov 1, 2019 at 14:28

11 Answers 11

171

Try the following:

import time
timeout = time.time() + 60*5   # 5 minutes from now
while True:
    test = 0
    if test == 5 or time.time() > timeout:
        break
    test = test - 1

You may also want to add a short sleep here so this loop is not hogging CPU (for example time.sleep(1) at the beginning or end of the loop body).

3
  • 5
    I use timeit.default_timer, which is always the most precise clock for the platform. In particular, time.time only has 1/60 s granularity on Windows, which may not be enough if you have a very short timeout. On Python 3, there is also time.monotonic, time.perf_counter, and time.process_time, which may be better (I've not dealt with any of them much).
    – jpkotta
    Jul 1, 2016 at 20:19
  • 19
    There are too many conditions to process in this solution. This should be optimized and the while True replaced with while time.time() < timeout. See the second following solution. Mar 13, 2017 at 16:46
  • 2
    Note, this code eats 100% of a core of your CPU! Jul 26, 2021 at 13:29
61

You do not need to use the while True: loop in this case. There is a much simpler way to use the time condition directly:

import time

# timeout variable can be omitted, if you use specific value in the while condition
timeout = 300   # [seconds]

timeout_start = time.time()

while time.time() < timeout_start + timeout:
    test = 0
    if test == 5:
        break
    test -= 1
6
  • 9
    Why do you not mention the probable need to stop the CPU from being loaded down by this function by using a sleep? This code may work, but it would needlessly use system resources.
    – Anthony
    May 1, 2017 at 19:46
  • Anthony, can you explain why it would "needlessly use system resources?" I'm genuinely curious & a bit confused; no one else above this used time.sleep(). Why would putting the logic-check in the while statement hog system resources?
    – Rob Truxal
    May 17, 2017 at 15:57
  • Good point about about putting the timeout check in the while loop condition. As for the comment about time.sleep(), the code does no work so will be cycling through the loop as fast as it can go. By adding a short time.sleep() the CPU can go idle instead of running at full power while doing no work.
    – Baldrickk
    Jun 21, 2017 at 8:54
  • 2
    I don't understand what you mean by "good point." Checking the logic of when to break out of the loop doesn't take more or less time regardless of where you put it and that wasn't what I was addressing. Constructs like while True aren't good form when the intention is not to stay in the loop forever. It can be misleading and there are better ways to do it even though, technically, it can be used to accomplish the same goal. It's kind of like eating steak with a knife and spoon.
    – Anthony
    Jun 23, 2017 at 13:07
  • 3
    This should almost certainly be using a monotonic time source. May 10, 2018 at 12:41
50

Try this module: http://pypi.python.org/pypi/interruptingcow/

from interruptingcow import timeout
try:
    with timeout(60*5, exception=RuntimeError):
        while True:
            test = 0
            if test == 5:
                break
            test = test - 1
except RuntimeError:
    pass
7
  • 2
    This solution should be the accepted answer in my opinion. It performs much better, ie. much more code executes inside the loop during a given period. Sep 23, 2015 at 21:14
  • 3
    Doesn't this cause the CPU to spin in the loop given the lack of a call to sleep?
    – Anthony
    May 1, 2017 at 19:48
  • 1
    Why is this solution with raising RuntimeError exception better than the proper condition in while cycle? Jul 11, 2017 at 9:57
  • 6
    This won't work on Windows because Windows doesn't implement SIGALRM. stackoverflow.com/a/52779986/1768141
    – Vinayak
    Nov 5, 2019 at 19:53
  • 1
    @Vinayak correct and confirmed :( Thanks nonetheless
    – Bryan_C
    Jul 13, 2023 at 21:18
31

Petr Krampl's answer is the best in my opinion, but more needs to be said about the nature of loops and how to optimize the use of the system. Beginners who happen upon this thread may be further confused by the logical and algorithmic errors in the question and existing answers.

First, let's look at what your code does as you originally wrote it:

while True:
    test = 0
    if test == 5:
        break
    test = test - 1

If you say while True in a loop context, normally your intention is to stay in the loop forever. If that's not your intention, you should consider other options for the structure of the loop. Petr Krampl showed you a perfectly reasonable way to handle this that's much more clear to someone else who may read your code. In addition, it will be more clear to you several months later should you need to revisit your code to add or fix something. Well-written code is part of your documentation. There are usually multiple ways to do things, but that doesn't make all of the ways equally valid in all contexts. while true is a good example of this especially in this context.

Next, we will look at the algorithmic error in your original code. The very first thing you do in the loop is assign 0 to test. The very next thing you do is to check if the value of test is 5, which will never be the case unless you have multiple threads modifying the same memory location. Threading is not in scope for this discussion, but it's worth noting that the code could technically work, but even with multiple threads a lot would be missing, e.g. semaphores. Anyway, you will sit in this loop forever regardless of the fact that the sentinel is forcing an infinite loop.

The statement test = test - 1 is useless regardless of what it does because the variable is reset at the beginning of the next iteration of the loop. Even if you changed it to be test = 5, the loop would still be infinite because the value is reset each time. If you move the initialization statement outside the loop, then it will at least have a chance to exit. What you may have intended was something like this:

test = 0
while True:
    test = test - 1
    if test == 5:
        break

The order of the statements in the loop depends on the logic of your program. It will work in either order, though, which is the main point.

The next issue is the potential and probable logical error of starting at 0, continually subtracting 1, and then comparing with a positive number. Yes, there are occasions where this may actually be what you intend to do as long as you understand the implications, but this is most likely not what you intended. Newer versions of python will not wrap around when you reach the 'bottom' of the range of an integer like C and various other languages. It will let you continue to subtract 1 until you've filled the available memory on your system or at least what's allocated to your process. Look at the following script and the results:

test = 0

while True:
    test  -= 1

    if test % 100 == 0:
        print "Test = %d" % test

    if test == 5:
        print "Test = 5"
        break

which produces this:

Test = -100
Test = -200
Test = -300
Test = -400
...
Test = -21559000
Test = -21559100
Test = -21559200
Test = -21559300
...

The value of test will never be 5, so this loop will never exit.

To add to Petr Krampl's answer, here's a version that's probably closer to what you actually intended in addition to exiting the loop after a certain period of time:

import time

test = 0
timeout = 300   # [seconds]

timeout_start = time.time()

while time.time() < timeout_start + timeout:
    if test == 5:
        break
    test -= 1

It still won't break based on the value of test, but this is a perfectly valid loop with a reasonable initial condition. Further boundary checking could help you to avoid execution of a very long loop for no reason, e.g. check if the value of test is less than 5 upon loop entry, which would immediately break the loop.


One other thing should be mentioned that no other answer has addressed. Sometimes when you loop like this, you may not want to consume the CPU for the entire allotted time. For example, say you are checking the value of something that changes every second. If you don't introduce some kind of delay, you would use every available CPU cycle allotted to your process. That's fine if it's necessary, but good design will allow a lot of programs to run in parallel on your system without overburdening the available resources. A simple sleep statement will free up the vast majority of the CPU cycles allotted to your process so other programs can do work.

The following example isn't very useful, but it does demonstrate the concept. Let's say you want to print something every second. One way to do it would be like this:

import time

tCurrent = time.time()

while True:
    if time.time() >= tCurrent + 1:
        print "Time = %d" % time.time()
        tCurrent = time.time()

The output would be this:

Time = 1498226796
Time = 1498226797
Time = 1498226798
Time = 1498226799

And the process CPU usage would look like this:

enter image description here

That's a huge amount of CPU usage for doing basically no work. This code is much nicer to the rest of the system:

import time

tCurrent = time.time()

while True:
    time.sleep(0.25) # sleep for 250 milliseconds
    if time.time() >= tCurrent + 1:
        print "Time = %d" % time.time()
        tCurrent = time.time()

The output is the same:

Time = 1498226796
Time = 1498226797
Time = 1498226798
Time = 1498226799

and the CPU usage is way, way lower:

enter image description here

2
  • tl;dr would be helpful
    – techkuz
    Nov 1, 2019 at 14:22
  • 5
    @techkuz This is a fundamental concept to computing in general. Skipping some of the details in my answer could provide an incomplete understanding to a new programmer. I am usually fine with tl;drs, but I think it would do more harm than good in this case. I will write one in this comment, though: TL;DR: Use sleep() or something similar to release resources when you don't need them constantly.
    – Anthony
    Nov 1, 2019 at 18:09
7
import time

abort_after = 5 * 60
start = time.time()

while True:
  delta = time.time() - start
  if delta >= abort_after:
    break
1
  • Thanks a lot @Fabian for your suggestons!
    – Ranjan Pal
    Apr 18, 2021 at 3:53
2

Old Thread, but I just want to share my re-usable solution using an Iterator:

class Timeoutloop:
    """ universal for time-out loop """
    def __init__(self, timeout):
        self.timeout = timeout
    def __iter__(self):
        self.start_time = time.time()
        return self
    def __next__(self):
        now = time.time()
        time_passed = now - self.start_time
        if time_passed > self.timeout: 
            raise StopIteration
        else:
            return time_passed

## usage example:
for passed in Timeoutloop(10.0):
    print(passed)
    sleep(random.random())
1

I want to share the one I am using:

import time
# provide a waiting-time list:
lst = [1,2,7,4,5,6,4,3]
# set the timeout limit
timeLimit = 4

for i in lst:
    timeCheck = time.time()
    while True:
        time.sleep(i)
        if time.time() <= timeCheck + timeLimit:
            print ([i,'looks ok'])
            break
        else:
            print ([i,'too long'])
            break

Then you will get:

[1, 'looks ok']
[2, 'looks ok']
[7, 'too long']
[4, 'looks ok']
[5, 'too long']
[6, 'too long']
[4, 'looks ok']
[3, 'looks ok']
0

I have read this but I just want to ask something, wouldn't something like I have written work at all? I have done the testing for 5,10 and 20 seconds. Its time isn't exactly accurate but they are really close to the actual values.

import time

begin_time=0
while begin_time<5:

    begin_time+=1
    time.sleep(1.0)
print("The Work is Done")
1
  • 2
    To get help with this, you should open a new question.
    – dspencer
    Mar 16, 2020 at 14:53
0

i'm not an python expert but i wrote small function to check timeout and brake while loop

# compare now and given times older than delta
def is_time_older_than(time, delta):
    print(dt.utcnow() - time,delta)
    if (dt.utcnow() - time) > delta: 
        return True
    return False
startTime = dt.utcnow()
while True:
           print("waiting")
           if (is_time_older_than(startTime, timedelta(seconds=5)) == True):
                break

you can record time before execution than send to function as starting time with delta value in seconds (or 60*1 for a minute) it will compare difference and return False or True thats it

0

Try the following:

from datetime import datetime, timedelta

end_time = datetime.now() + timedelta(minutes=5)
while True:
    current_time = datetime.now()
    if current_time == end_time:
        break
0

Inspired by @andrefsp's answer, here's a simple context manager that gives a deadline:

from contextlib import contextmanager


@contextmanager
def deadline(timeout_seconds: Union[int, float]):
    """
    Context manager that gives a deadline to run some code.

    Usage:
        `with deadline(secs): ...`

        or

        ```
        @deadline(secs)
        def func: ...
        ```

    Args:
        timeout_seconds: number of seconds before context manager raises a DeadlineExceededException

    Raises:
        DeadlineExceededException error if more than timeout_seconds elapses.
    """
    start_time = time.time()
    yield
    elapsed_time = time.time() - start_time
    if elapsed_time > timeout_seconds:
        msg = f"Deadline of {timeout_seconds} seconds exceeded by {elapsed_time - timeout_seconds} seconds"
        logger.exception(msg)
        raise DeadlineExceededException(msg)


class DeadlineExceededException(Exception):
    pass

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.