【问题标题】:break/interrupt a time.sleep() in python在 python 中中断/中断 time.sleep()
【发布时间】:2011-07-04 02:41:37
【问题描述】:

我需要使用 ctrl c 中断 time.sleep()。

While 1:
    time.sleep(60)

在上面的代码中,当控件进入 time.sleep 函数时,python 需要整整 60 秒才能处理 CTRL C

有什么优雅的方法可以做到这一点。这样即使控制在 time.sleep 功能中我也可以中断

编辑

我在 Windows 2000 上使用 python 2.2 的遗留实现上对其进行了测试,这导致了所有麻烦。如果我使用更高版本的 python CTRL C 会中断 sleep() 。我通过在 for 循环中调用 sleep(1) 进行了快速破解。暂时解决了我的问题

【问题讨论】:

  • 没有代码是用来做一些监控的。它每 60 秒监控一次,因此为了退出 pgm,用户按下 CTRL C 但他必须等待 60 秒才能恢复提示。
  • 在大多数系统中,Ctrl-C 会中断睡眠。在 Unix 和 Mac 上当然可以。你在哪个系统上?
  • for i in range(60): sleep(1) 我认为它更好

标签: python


【解决方案1】:

正确的答案是使用python stdlib的threading.Event

当然你可以调低你的睡眠间隔,让你的睡眠时间很短,但是如果你实际上想要每 60 秒运行一次循环呢?然后你需要做更多的工作来确定是该跑步还是继续睡觉。此外,您仍然在技术上阻止,但只是很短的一段时间。对比threading.Event

from threading import Event

exit = Event()

def main():
    while not exit.is_set():
      do_my_thing()
      exit.wait(60)

    print("All done!")
    # perform any cleanup here

def quit(signo, _frame):
    print("Interrupted by %d, shutting down" % signo)
    exit.set()

if __name__ == '__main__':

    import signal
    for sig in ('TERM', 'HUP', 'INT'):
        signal.signal(getattr(signal, 'SIG'+sig), quit);

    main()

当信号处理程序调用exit.set() 时,主线程的wait() 调用将立即被中断。

现在,您可以使用Event 来表示还有更多工作要做等。但在这种情况下,它作为我们想要退出的方便指示符起到双重作用(例如while not exit.is_set() 部分。)

您还可以选择在 while 循环之后放置任何清理代码。

【讨论】:

  • 注意:调用exit.set()后,exit.wait()调用将不再对事件对象exit起作用,除非你调用exit.clear(),参考:threading::Event Objects
  • 我认为这会调用do_my_thing(),每 60+ 次do_my_thing() 执行一次。所以如果do_my_thing()总是需要5秒,它会每65秒执行一次。
  • @desowin 正确...要使其每 60 秒(大约)触发一次,您应该将变量设置为第一次触发start_time = time.time(),在循环中放置一个计数器cycle_num += 1,并做wait(max(0, start_time+60*cycle_num-time.time())) 而不是仅仅60。max 部分只是为了避免负等待时间。当然,您也应该将 60 设为可变参数;哦,不要调用函数exit
  • @travc 另外应该注意的是 time.monotonic() 应该优先于 time.time()。调整/更改系统时钟时,使用 time.time() 会中断计时。
  • 这很漂亮。
【解决方案2】:

不确定这段代码的含义是什么 - 但如果需要,请使用更短的 sleep() 间隔并在其周围放置一个 for 循环:

for i in range(60):
   sleep(1)

使用 try..except 捕获 KeyboardInterrupt 异常非常简单

【讨论】:

  • 这可以扩展以获得更快的反应:for i in range(sec/0.1): sleep(0.1)
  • 这可能适用于他的幼稚示例,但在很多情况下不能简单地进行更快的轮询。
  • 这是一个黑客。请参阅@thom_nic 的正确答案。
  • 这种反复尝试的方法称为“轮询”。应尽可能避免它,因为它通常会导致 CPU 资源的浪费。系统提供替代机制,例如 @thorn_nic 使用的机制。它们非常有效,因为它们在内部使用了特殊的硬件指令和中断。
  • 解决方案如下:` for i in range(360): try: sleep(1) except KeyboardInterrupt: sys.exit(0)`
【解决方案3】:

当用户按下中断键 Ctrl-C 时会引发 KeyboardInterrupt 异常。在 python 中,这是从 SIGINT 信号转换而来的。这意味着,您可以使用信号模块随心所欲地处理它:

import signal

def handler(signum, frame):
    print("do whatever, like call thread.interrupt_main()")

signal.signal(signal.SIGINT, handler)
print("Waiting for SIGINT...")
signal.pause()

这样,您可以在收到键盘中断时做任何您想做的事情。

【讨论】:

  • 只有在从睡眠中唤醒后才会处理中断.. :(
  • 我认为您的代码中可能还有其他内容,因为我无法重现它,或者您可以在此处的标准文档中看到:docs.python.org/library/time.html#time.sleep 例如,这发生在一个不同的线程,也许不是后台线程?
  • 这仅适用于只有一个主线程的情况。如果您启动了其他线程,则信号处理程序将不会处理其他正在休眠的线程。
  • 不幸的是,这不会中断time.sleep() 调用,即使您的信号处理函数将被执行。唯一的选择是从信号句柄中调用sys.exit(),但这是非常暴力的。
  • sleep() 在 C 中可以被信号处理程序中断,但在 Python 中不会。引用来自官方 Python 3.7 文档,“在 3.5 版中已更改:即使睡眠被信号中断,该函数现在至少睡眠几秒钟,除非信号处理程序引发异常(有关基本原理,请参见 PEP 475)。 "
【解决方案4】:

我在 Linux 下使用 python 版本 2.5、2.6、3 尝试了您的代码,并且在按下 CTRL-C 时都抛出“KeyboardInterrupt”异常。

也许一些异常处理捕获了中断或者你的问题是这样的: Why is KeyboardInterrupt not working in python?

【讨论】:

    【解决方案5】:

    最优雅的解决方案当然是threading.Event,不过如果您只需要快速破解,这段代码也很有效:

    import time
    
    def main():
        print("It’s time !")
    
    if __name__ == "__main__":
        print("press ctrl-c to stop")
        loop_forever = True
        while loop_forever:
            main()
            try:
                time.sleep(60)
            except KeyboardInterrupt:
                loop_forever = False
    

    【讨论】:

    • 在我的测试中,在使用 Python 3.8 的 Linux 上,当我在睡眠期间按 control-c 时,直到睡眠完成后才调用 KeyboardInterrupt。
    • 奇怪的是它不适合你。我在 macOS、Raspberry Pi OS 和 Ubuntu 上尝试过,它对我有用。
    【解决方案6】:

    基于@Andreas Jung 的回答

            for i in range(360):
                try:
                    sleep(1)
                except KeyboardInterrupt:
                    sys.exit(0)
    

    【讨论】:

      【解决方案7】:

      我想我会把这个扔进去。

      import time
      
      
      def sleep(seconds):
          for i in range(seconds):
              try:
                  time.sleep(1)
              except KeyboardInterrupt:
                  print("Oh! You have sent a Keyboard Interrupt to me.\nBye, Bye")
                  break
      
      
      sleep(60)
      

      【讨论】:

      • 我认为您的意思是使用“休息”而不是“继续”来停止睡眠。使用 continue 会导致它跳到循环的下一次迭代,这本质上是一个 noop。
      猜你喜欢
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      • 2017-08-01
      • 1970-01-01
      • 2018-01-24
      • 1970-01-01
      • 1970-01-01
      • 2010-11-19
      相关资源
      最近更新 更多