如何在 Python 中安全地停止无限循环?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32922909/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to stop an infinite loop safely in Python?
提问by davegri
I've got a script that runs on a infinite loop and adds things to a database and does things that I can't just stop halfway through so I can't just press ctrl+C and stop it.
我有一个脚本,它在无限循环中运行并将内容添加到数据库中,并执行我不能中途停止的事情,因此我不能按 ctrl+C 并停止它。
I want to be able to somehow stop a while loop but let it finish it's last iteration before it stops.
我希望能够以某种方式停止 while 循环,但让它在停止之前完成最后一次迭代。
Let me clarify:
让我澄清一下:
my code looks something like this:
我的代码看起来像这样:
while True:
does something
does more things
does more things
I want to be able to interrupt the while loop at the end, or the beginning, but not between doing things because that would be bad.
我希望能够在结束或开始时中断 while 循环,但不能在做事之间中断,因为那样会很糟糕。
and I don't want it to ask me after every iteration if i want to continue.
我不希望它在每次迭代后问我是否要继续。
thanks for the great answers, i'm super grateful but my implementation doesn't seem to be working:
感谢您提供出色的答案,我非常感激,但我的实现似乎不起作用:
def signal_handler(signal, frame):
global interrupted
interrupted = True
class Crawler():
def __init__(self):
# not relevent
def crawl(self):
interrupted = False
signal.signal(signal.SIGINT, signal_handler)
while True:
doing things
more things
if interrupted:
print("Exiting..")
break
when I press ctr+c the program just keeps going ignoring me
当我按 ctr+c 时,程序一直不理我
采纳答案by alexis
What you need to do is catch the interrupt, set a flag saying you were interrupted but then continue working until it's time to check the flag (at the end of each loop). Because python's try-except construct will abandon the current run of the loop, you need to set up a proper signal handler; it'll handle the interrupt but then let python continue where it left off. Here's how:
您需要做的是捕捉中断,设置一个标志,表示您被中断了,但然后继续工作,直到检查标志的时间(在每个循环结束时)。因为python的try-except构造会放弃循环的当前运行,所以需要设置一个合适的信号处理程序;它会处理中断,然后让 python 从它停止的地方继续。就是这样:
import signal
import time # For the demo only
def signal_handler(signal, frame):
global interrupted
interrupted = True
signal.signal(signal.SIGINT, signal_handler)
interrupted = False
while True:
print("Working hard...")
time.sleep(3)
print("All done!")
if interrupted:
print("Gotta go")
break
Notes:
笔记:
Use this from the command line. In the IDLE console, it'll trample on IDLE's own interrupt handling.
A better solution would be to "block" KeyboardInterrupt for the duration of the loop, and unblock it when it's time to poll for interrupts. This is a feature of some Unix flavors but not all, hence python does not support it(see the third "General rule")
The OP wants to do this inside a class. But the interrupt function is invoked by the signal handling system, with two arguments: The signal number and a pointer to the stack frame-- no place for a
self
argument giving access to the class object. Hence the simplest way to set a flag is to use a global variable. You can rig a pointer to the local context by using closures (i.e., define the signal handler dynamically in__init__()
, but frankly I wouldn't bother unless a global is out of the question due to multi-threading or whatever.
从命令行使用它。在 IDLE 控制台中,它会践踏 IDLE 自己的中断处理。
更好的解决方案是在循环期间“阻塞”KeyboardInterrupt,并在轮询中断时解除阻塞。这是一些 Unix 风格的一个特性,但不是全部,因此 python不支持它(参见第三个“一般规则”)
OP 想在课堂上做到这一点。但是中断函数是由信号处理系统调用的,它有两个参数:信号编号和一个指向堆栈帧的指针——没有地方放置
self
可以访问类对象的参数。因此,设置标志的最简单方法是使用全局变量。您可以通过使用闭包来装配指向本地上下文的指针(即,在 中动态定义信号处理程序__init__()
,但坦率地说,除非由于多线程或其他原因无法使用全局变量,否则我不会打扰。
Caveat:If your process is in the middle of a system call, handling an signal may interrupt the system call. So this may not be safe for all applications. Safer alternatives would be (a) Instead of relying on signals, use a non-blocking read at the end of each loop iteration (and type input instead of hitting ^C); (b) use threads or interprocess communication to isolate the worker from the signal handling; or (c) do the work of implementing real signal blocking, if you are on an OS that has it. All of them are OS-dependent to some extent, so I'll leave it at that.
警告:如果您的进程处于系统调用的中间,则处理信号可能会中断系统调用。所以这对所有应用程序来说可能并不安全。更安全的替代方法是 (a) 不依赖信号,而是在每次循环迭代结束时使用非阻塞读取(并输入输入而不是点击 ^C);(b) 使用线程或进程间通信将工作者与信号处理隔离;或 (c) 执行实现真正信号阻塞的工作,如果您使用的是具有它的操作系统。所有这些在某种程度上都依赖于操作系统,所以我将就此搁置。
回答by praba230890
the below logic will help you do this,
下面的逻辑将帮助你做到这一点,
import signal
import sys
import time
run = True
def signal_handler(signal, frame):
global run
print "exiting"
run = False
signal.signal(signal.SIGINT, signal_handler)
while run:
print "hi"
time.sleep(1)
# do anything
print "bye"
while running this, try pressing CTRL+C
运行此程序时,请尝试按 CTRL+C
回答by pdave
I hope below code would help you:
我希望下面的代码可以帮助你:
#!/bin/python
import sys
import time
import signal
def cb_sigint_handler(signum, stack):
global is_interrupted
print "SIGINT received"
is_interrupted = True
if __name__ == "__main__":
is_interrupted = False
signal.signal(signal.SIGINT, cb_sigint_handler)
while(1):
# do stuff here
print "processing..."
time.sleep(3)
if is_interrupted:
print "Exiting.."
# do clean up
sys.exit(0)
回答by Anthony Scemama
To clarify @praba230890's solution: The interrupted
variable was not defined in the correct scope. It was defined in the crawl
function and the handler could not reach it as a global variable, according to the definition of the handler at the root of the program.
澄清@praba230890 的解决方案:interrupted
变量未在正确的范围内定义。它是在crawl
函数中定义的,根据程序根部处理程序的定义,处理程序无法将其作为全局变量访问。