¿Cómo detendría un bucle while después de n cantidad de tiempo?


¿Cómo podría parar un bucle while después de 5 minutos si no logra lo que quiero que logre?

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

Este código me lanza en un bucle sin fin.

Author: royB, 2012-11-08

6 answers

Intente lo siguiente:

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

Es posible que también desee agregar un corto sueño aquí para que este bucle no acapare la CPU (por ejemplo time.sleep(1) al principio o al final del cuerpo del bucle).

 73
Author: Andrew Clark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-08 16:30:41

Probar este módulo: 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

Saludos

 29
Author: andrefsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-10 14:27:14

No es necesario utilizar el bucle while True: en este caso. Hay una forma mucho más sencilla de usar la condición de tiempo directamente:

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
 26
Author: Petr Krampl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-13 16:52:51

La respuesta de Petr Krampl es la mejor en mi opinión, pero hay que decir más sobre la naturaleza de los bucles y cómo optimizar el uso del sistema. Los principiantes que se encuentran con este hilo pueden confundirse aún más por los errores lógicos y algorítmicos en la pregunta y las respuestas existentes.

Primero, veamos lo que hace tu código como lo escribiste originalmente: {[18]]}

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

Si dices while True en un contexto de bucle, normalmente tu intención es permanecer en el bucle para siempre. Si eso no es su intención, usted debe considerar otras opciones para la estructura del bucle. Petr Krampl te mostró una manera perfectamente razonable de manejar esto que es mucho más clara para alguien más que pueda leer tu código. Además, será más claro para usted varios meses más tarde si necesita revisar su código para agregar o arreglar algo. El código bien escrito es parte de su documentación. Por lo general, hay varias formas de hacer las cosas, pero eso no hace que todas las formas sean igualmente válidas en todos los contextos. while true es un buen ejemplo de esto especialmente en este contexto.

A continuación, veremos el error algorítmico en su código original. Lo primero que haces en el bucle es asignar 0 a test. Lo siguiente que debe hacer es comprobar si el valor de test es 5, lo que nunca será el caso a menos que tenga varios hilos modificando la misma ubicación de memoria. Threading no está en el alcance de esta discusión, pero vale la pena señalar que el código podría funcionar técnicamente, pero incluso con faltarían muchos hilos múltiples, por ejemplo, semáforos. De todos modos, te sentarás en este bucle para siempre, independientemente del hecho de que el centinela está forzando un bucle infinito.

La instrucción test = test - 1 es inútil independientemente de lo que haga porque la variable se restablece al principio de la siguiente iteración del bucle. Incluso si lo cambias a test = 5, el bucle seguiría siendo infinito porque el valor se restablece cada vez. Si mueve la instrucción de inicialización fuera del bucle, entonces al menos tendrá la oportunidad de salir. Lo que podrías haber pretendido era algo como esto:

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

El orden de las instrucciones en el bucle depende de la lógica de su programa. Funcionará en cualquier orden, sin embargo, que es el punto principal.

El siguiente problema es el error lógico potencial y probable de comenzar en 0, restar continuamente 1, y luego comparar con un número positivo. Sí, hay ocasiones en las que esto puede ser realmente lo que tiene la intención de hacer, siempre y cuando comprenda las implicaciones, pero lo más probable es que esto no sea lo que pretendía. Las versiones más recientes de python no se ajustarán cuando llegues a la 'parte inferior' del rango de un entero como C y varios otros lenguajes. Le permitirá continuar restando 1 hasta que haya llenado la memoria disponible en su sistema o al menos lo que está asignado a su proceso. Mira el siguiente script y los resultados:

test = 0

while True:
    test  -= 1

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

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

Que produce esto:

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

El valor de test nunca será 5, así que este bucle nunca saldrá.

Para agregar a la respuesta de Petr Krampl, aquí hay una versión que probablemente esté más cerca de lo que realmente pretendía, además de salir del bucle después de un cierto período de tiempo:

import time

test = 0
timeout = 300   # [seconds]

timeout_start = time.time()

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

Todavía no se romperá basado en el valor de test, pero este es un bucle perfectamente válido con una condición inicial razonable. La comprobación adicional de los límites podría ayudarle a evitar la ejecución de un bucle muy largo sin ninguna razón, por ejemplo, comprobar si el valor de la prueba es inferior a 5 sobre entrada de bucle, que rompería inmediatamente el bucle.


Otra cosa debe mencionarse que ninguna otra respuesta ha abordado. A veces, cuando se realiza un bucle como este, es posible que no desee consumir la CPU durante todo el tiempo asignado. Por ejemplo, supongamos que está comprobando el valor de algo que cambia cada segundo. Si no introduce algún tipo de retraso, utilizaría todos los ciclos de CPU disponibles asignados a su proceso. Eso está bien si es necesario, pero un buen diseño permitirá un muchos programas para ejecutar en paralelo en su sistema sin sobrecargar los recursos disponibles. Una simple declaración de suspensión liberará la gran mayoría de los ciclos de CPU asignados a su proceso para que otros programas puedan funcionar.

El siguiente ejemplo no es muy útil, pero demuestra el concepto. Digamos que quieres imprimir algo cada segundo. Una forma de hacerlo sería así:

import time

tCurrent = time.time()

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

La salida sería la siguiente:

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

Y el uso de la CPU del proceso se vería así:

introduzca la descripción de la imagen aquí

Eso es una gran cantidad de uso de CPU para hacer básicamente ningún trabajo. Este código es mucho más agradable para el resto del sistema:

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()

La salida es la misma:

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

Y el uso de CPU es mucho, mucho menor:

introduzca la descripción de la imagen aquí

 5
Author: Anthony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-23 14:12:48
import time

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

while True:
  delta = time.time() - start
  if delta >= abort_after:
    break
 4
Author: Fabian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-08 16:31:11

Quiero compartir el que estoy usando:

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

Entonces obtendrás:

[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
Author: kennyut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-28 21:19:04