【问题标题】:How would I stop a while loop after n amount of time?在 n 时间后,我将如何停止 while 循环?
【发布时间】:2012-10-28 21:34:40
【问题描述】:

如果 while 循环没有达到我想要的效果,我将如何在 5 分钟后停止它。

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

这段代码让我陷入无限循环。

【问题讨论】:

  • 你会陷入死循环,因为最后一行必须是:test = test +1
  • 这很有趣,代码本身没有任何意义(如果条件不可达),但除了@MichaelKüller 之外的所有人都没有注意到。

标签: python python-2.7


【解决方案1】:

我读过这篇文章,但我只是想问一些事情,像我这样写的东西根本就不会写吗? 我已经完成了 5,10 和 20 秒的测试。它的时间并不完全准确,但它们确实接近实际值。

import time

begin_time=0
while begin_time<5:

    begin_time+=1
    time.sleep(1.0)
print("The Work is Done")

【讨论】:

  • 要获得这方面的帮助,您应该打开一个新问题。
【解决方案2】:

试试这个模块: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

【讨论】:

  • 我认为这个解决方案应该是公认的答案。它表现得更好,即。在给定的时间段内,更多的代码在循环内执行。
  • 由于没有调用睡眠,这不会导致 CPU 在循环中旋转吗?
  • @Anthony 在这个特殊的例子中是的,直到超时开始并停止它。这只是展示如何使用此特定模块停止循环的示例。
  • 为什么这个引发 RuntimeError 异常的解决方案比 while 循环中的适当条件更好?
  • 这在 Windows 上不起作用,因为 Windows 没有实现 SIGALRM。 stackoverflow.com/a/52779986/1768141
【解决方案3】:

我想分享我正在使用的一个:

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

然后你会得到:

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

【讨论】:

    【解决方案4】:

    Petr Krampl 的答案在我看来是最好的,但是关于循环的性质以及如何优化系统的使用,还需要多说一些。遇到此线程的初学者可能会被问题和现有答案中的逻辑和算法错误进一步混淆。

    首先,让我们看看你的代码在你最初编写的时候做了什么:

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

    如果您在循环上下文中说while True,通常您的意图是永远留在循环中。如果这不是您的意图,您应该考虑循环结构的其他选项。 Petr Krampl 向您展示了一种非常合理的方法来处理这个问题,这对于可能阅读您代码的其他人来说更加清楚。此外,如果您需要重新访问代码以添加或修复某些内容,几个月后您会更加清楚。编写良好的代码是您文档的一部分。通常有多种方法可以做事,但这并不能使所有方法在所有情况下都同样有效。 while true 就是一个很好的例子,尤其是在这种情况下。

    接下来,我们将查看原始代码中的算法错误。您在循环中做的第一件事就是将 0 分配给 test。接下来要做的是检查test 的值是否为5,除非您有多个线程修改相同的内存位置,否则永远不会出现这种情况。线程不在本次讨论的范围内,但值得注意的是代码在技术上可以工作,但即使有多个线程也会丢失很多,例如信号量。无论如何,无论哨兵强制执行无限循环,您都将永远处于此循环中。

    test = test - 1 语句不管它做什么都是无用的,因为变量在循环的下一次迭代开始时被重置。即使您将其更改为test = 5,循环仍然是无限的,因为每次都会重置该值。如果将初始化语句移到循环之外,那么它至少有机会退出。你的意图可能是这样的:

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

    循环中语句的顺序取决于程序的逻辑。不过,它可以按任意顺序工作,这是重点。

    下一个问题是从 0 开始,不断减 1,然后与正数比较的潜在和可能的逻辑错误。是的,在某些情况下,只要您了解其中的含义,这实际上可能就是您打算做的事情,但这很可能不是您想要做的。当您到达整数范围的“底部”(如 C 和其他各种语言)时,较新版本的 python 不会环绕。它会让您继续减 1,直到您填满系统上的可用内存或至少分配给您的进程的内存。看看下面的脚本和结果:

    test = 0
    
    while True:
        test  -= 1
    
        if test % 100 == 0:
            print "Test = %d" % test
    
        if test == 5:
            print "Test = 5"
            break
    

    产生这个:

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

    test 的值永远不会是 5,所以这个循环永远不会退出。

    为了补充 Petr Krampl 的答案,除了在一段时间后退出循环之外,这里的版本可能更接近您的实际意图:

    import time
    
    test = 0
    timeout = 300   # [seconds]
    
    timeout_start = time.time()
    
    while time.time() < timeout_start + timeout:
        if test == 5:
            break
        test -= 1
    

    根据test 的值,它仍然不会中断,但这是一个具有合理初始条件的完全有效的循环。进一步的边界检查可以帮助您避免无缘无故执行很长的循环,例如检查循环进入时test的值是否小于5,这将立即中断循环。


    应该提到另一件事,没有其他答案已经解决。有时当你像这样循环时,你可能不想在整个分配的时间内消耗 CPU。例如,假设您正在检查每秒变化的事物的值。如果您不引入某种延迟,您将使用分配给您的进程的每个可用 CPU 周期。如果有必要,那很好,但是好的设计将允许许多程序在您的系统上并行运行,而不会增加可用资源的负担。一个简单的 sleep 语句将释放分配给您的进程的绝大多数 CPU 周期,以便其他程序可以工作。

    以下示例不是很有用,但它确实演示了这个概念。假设您想每秒打印一些内容。一种方法是这样的:

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

    输出是这样的:

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

    进程的 CPU 使用率如下所示:

    这对于基本上不做任何工作来说是一个巨大的 CPU 使用量。这段代码对系统的其他部分更好:

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

    输出是一样的:

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

    而且 CPU 使用率要低得多:

    【讨论】:

    • tl;博士会有所帮助
    • @techkuz 这是一般计算的基本概念。跳过我的回答中的一些细节可能会给新程序员提供不完整的理解。我通常对 tl;drs 很好,但我认为在这种情况下它弊大于利。不过,我会在此评论中写一个:TL;DR: Use sleep() or something similar to release resources when you don't need them constantly.
    【解决方案5】:

    在这种情况下,您不需要使用 while True: 循环。有一种更简单的方法可以直接使用时间条件:

    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
    

    【讨论】:

    • 你为什么不提到可能需要通过使用睡眠来阻止 CPU 被这个函数加载?此代码可能有效,但会不必要地占用系统资源。
    • Anthony,你能解释一下为什么它会“不必要地使用系统资源”吗?我真的很好奇,有点困惑;在此之上没有其他人使用 time.sleep()。为什么将逻辑检查放在 while 语句中会占用系统资源?
    • 关于将超时检查置于 while 循环条件中的要点。至于关于time.sleep() 的评论,代码不起作用,因此将尽可能快地循环循环。通过添加一个简短的time.sleep(),CPU 可以闲置而不是在不工作时全功率运行。
    • 我不明白你所说的“好点”是什么意思。无论你把它放在哪里,检查何时跳出循环的逻辑都不会花费更多或更少的时间,这不是我要解决的问题。当意图不是永远留在循环中时,像while True 这样的构造不是很好的形式。它可能具有误导性,并且有更好的方法来做到这一点,即使从技术上讲,它可以用来实现相同的目标。这有点像用刀和勺子吃牛排。
    • 这几乎可以肯定是使用monotonic time source
    【解决方案6】:
    import time
    
    abort_after = 5 * 60
    start = time.time()
    
    while True:
      delta = time.time() - start
      if delta >= abort_after:
        break
    

    【讨论】:

    • 非常感谢@Fabian 的建议!
    【解决方案7】:

    尝试以下方法:

    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
    

    您可能还想在此处添加一个短暂的睡眠,这样该循环就不会占用 CPU(例如,time.sleep(1) 在循环体的开头或结尾)。

    【讨论】:

    • 我使用timeit.default_timer,它始终是平台最精确的时钟。特别是,time.time 在 Windows 上只有 1/60 秒的粒度,如果超时时间很短,这可能还不够。在 Python 3 上,还有 time.monotonictime.perf_countertime.process_time,可能会更好(我没有过多地处理它们)。
    • 此解决方案中要处理的条件太多。这应该被优化并且while True替换为while time.time() &lt; timeout。请参阅以下第二个解决方案。
    • @Andrew Clark,您好,先生,我只是想问一下。是否可以在休息后继续 while 循环?如果有可能怎么办?谢谢,先生!
    • 注意,这段代码占用了你 CPU 100% 的内核!
    猜你喜欢
    • 2010-09-26
    • 2013-09-11
    • 2023-01-10
    • 1970-01-01
    • 2022-10-31
    • 2015-10-04
    • 1970-01-01
    • 2016-03-12
    • 1970-01-01
    相关资源
    最近更新 更多