简体   繁体   中英

How to break a while loop after some minutes?

I have a while loop in my python program and I want to break from this loop after 5 minutes, I'm currently using the following code:

start_time = time.time()
while time.time() - start_time < 300:
    # do something

However, it takes added time in each iteration to calculate the time.time() - start_time < 300: part. For example, if I use a for loop with 30000 iterations and it lasts 5 minutes, the above code does fewer iterations than that.

What should I do about it? I have to break the loop after 5 minutes and I just want to handle this by time, not by for loop iterations.

One of my friends suggested datetime package, but I do not know how it works.

the above code does fewer iterations than that.

A likely reason is that checking the time itself takes time. Therefore, you could check the time less often:

start_time = time.time()
lv = 0
while True:
    if lv == 100: # check time only once in 100 iterations
        if time.time() - start_time > 300:
            break
        else:
            lv = 0

    # do something

    lv +=1

It sounds like need of implementing timeout for me. You might use built-in threading module to do it following way:

import time
from threading import Thread, Event

stop = Event()

def function():
    while not stop.is_set():
        print('*')
        time.sleep(1)

thread = Thread(target=function)

thread.start()
thread.join(timeout=60)
stop.set()

Above code simply prints * every second for 60 seconds.

I think using threading and sleep is the best approach. You can do it in a simple way using a lambda function to create a sleeping background thread and simply check if if the thread is still running inside your loop (break the loop when time is up):

from threading import Thread
from time import sleep

fiveMinutes = Thread(target=lambda:sleep(300)) # 5 min sleeping thread (300 seconds)
fiveMinutes.start() # start wait time

for i in range(10**10): # <-- this represents your loop condition
    # ...
    if not fiveMinutes.is_alive(): break # break after 5 minutes elapsed
    # ...

This should add less overhead to your processing loop so the timed out iterations should be very close to the effective time to process the same number of iterations.

[EDIT] Thread/sleep vs time.time()

I made a few more tests and there is very little difference between using the sleeping thread and an optimal use of the time() function. The overhead is less than a microsecond per iteration:

from time import time

fiveMinutes = time() + 300

for i in range(10**10): # <-- this represents your loop condition
    # ...
    if time() > fiveMinutes : break # break after 5 minutes elapsed
    # ...

If you are getting a 3 seconds difference over a 1 minute sample, I would suspect that the processing time of your logic has its own variability (ie running the same number of iterations in a loop will vary from one run to the next). Either that or you are checking for timeout a lot more often than the 11,311 iterations would suggest (perhaps by checking inside a nested loop). In my tests the two approaches varied by less than 0.5 second over a 60 second sample executing 41,000 iterations: +0.37sec for time() and +0.23sec for thread.

(a bit of tinkering later, I think I broadly agree with Alain. I get a significant speed up from just breaking after a certain number of iterations, but whether using a thread, time.time directly or a partial sampling approach, pretty much all of them end up degrading loop time. time.time() itself is probably already fairly highly optimized, so all the extra booleans and stuff to avoid using it too much probably don't improve things much).

Don't know if it's more efficient, but you could try a thread and a shared mutable object to drive the loop. Here's a test at 0.5 seconds.

import time
from threading import Thread

def myfunc(mutable_):
    time.sleep(0.5)
    mutable_.value = False
    print("done")


class Mutable:
    value = True

mutable_ = Mutable()

t = Thread(target=myfunc, args=(mutable_,))

start = time.time()
t.start()
while mutable_.value:
    print(time.time() - start)

The unknown here to me is whether the loop's attribute access to mutable_.value , which doesn't need to involve the thread directly is that much cheaper than polling the thread itself.

Now, in my test, there was a bit of a difference where the thread had not yet updated the value so the loop ran a bit over compared to using own local time check. Might be possible to set your thread time out at 299.9 seconds, get close and finish more carefully.

Here's a v2 using thread sleeping to get most of the way there (that 0.49) and a, normally short-circuited time.time() check to finalize.

def myfunc(mutable_, looptime):
    time.sleep(looptime)
    mutable_.value = False

t = Thread(target=myfunc, args=(mutable_,.49))

t.start()
start = time.time()

while mutable_.value or (time.time() - start <= .5):
    print(time.time() - start)

Try this following:

start_time = time.time() + 300
while True:
    if time.time() > start_time:
         break

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM