繁体   English   中英

如何在 Python 中安全地停止无限循环?

[英]How to stop an infinite loop safely in Python?

我有一个脚本,它运行一个无限循环并将​​东西添加到数据库中,并且做一些我不能在中途停止的事情,所以我不能只按 Ctrl+C 并停止它。

我希望能够以某种方式停止一个while循环,但让它在它停止之前完成它的最后一次迭代。

让我澄清一下:

我的代码看起来像这样:

while True:
    do something
    do more things
    do more things

我希望能够在结束或开始时中断 while 循环,但不能在做事之间中断,因为那样会很糟糕。

而且我不希望它在每次迭代后询问我是否要继续。


感谢您的出色回答,我非常感激,但我的实施似乎没有奏效:

def signal_handler(signal, frame):
    global interrupted
    interrupted = True

class Crawler():
    def __init__(self):
        # not relevant

    def crawl(self):
        interrupted = False
        signal.signal(signal.SIGINT, signal_handler)
        while True:
            doing things
            more things

            if interrupted:
                print("Exiting..")
                break

当我按下 Ctrl+C 时,程序会一直忽略我。

你需要做的是捕捉中断,设置一个标志,说你被中断,然后继续工作,直到检查标志的时候(在每个循环结束时)。 因为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

笔记:

  1. 从命令行使用它。 在 IDLE 控制台中,它会践踏 IDLE 自己的中断处理。

  2. 更好的解决方案是在循环期间“阻塞”KeyboardInterrupt,并在轮询中断时解除阻塞。 这是一些 Unix 风格的特性,但不是全部,因此 python不支持它(参见第三条“一般规则”)

  3. OP 想在课堂上做到这一点。 但是中断函数是由信号处理系统调用的,带有两个参数:信号编号和指向堆栈帧的指针 - 没有用于访问类对象的self参数的位置。 因此,设置标志的最简单方法是使用全局变量。 您可以使用闭包来装配指向本地上下文的指针(即,在__init__()中动态定义信号处理程序,但坦率地说,除非由于多线程或其他原因而无法使用全局变量,否则我不会打扰。

警告:如果您的进程处于系统调用的中间,处理信号可能会中断系统调用。 因此,这可能对所有应用程序都不安全。 更安全的替代方案是 (a) 在每次循环迭代结束时使用非阻塞读取而不是依赖信号(并键入输入而不是按 ^C); (b) 使用线程或进程间通信将工作人员与信号处理隔离开来; 或 (c) 如果您在具有它的操作系统上,则执行实现真正信号阻塞的工作。 所有这些都在某种程度上依赖于操作系统,所以我将把它留在那里。

以下逻辑将帮助您做到这一点,

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

运行此程序时,请尝试按CTRL + C

我希望下面的代码可以帮助你:

#!/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 True:
        # do stuff here 
        print("processing...")
        time.sleep(3)
        if is_interrupted:
            print("Exiting..")
            # do clean up
            sys.exit(0)

澄清@praba230890 的解决方案: interrupted变量未在正确范围内定义。 它是在crawl函数中定义的,根据程序根处处理程序的定义,处理程序无法将其作为全局变量访问。

这是上述原理的编辑示例。 它是独立线程中的不定式 python 循环,安全信号结束。 还具有线程阻塞睡眠步骤 - 由您决定保留它,替换为 asyncio 实现或删除。 这个函数可以被导入到应用程序的任何地方,在不阻塞其他代码的情况下运行(例如,对 REDIS pusub 订阅有好处)。 在 SIGINT 捕获后,线程作业和平结束。

from typing import Callable
import time
import threading
import signal

end_job = False


def run_in_loop(job: Callable, interval_sec: int = 0.5):
    def interrupt_signal_handler(signal, frame):
        global end_job
        end_job = True

    signal.signal(signal.SIGINT, interrupt_signal_handler)

    def do_job():
        while True:
            job()
            time.sleep(interval_sec)

            if end_job:
                print("Parallel job ending...")
                break

    th = threading.Thread(target=do_job)
    th.start()

您忘记在抓取功能中添加全局语句。 所以结果将是

import signal

def signal_handler(signal, frame):
    global interrupted
    interrupted = True

class Crawler():
    def __init__(self):
        ... # or pass if you don't want this to do anything. ... Is for unfinished code

    def crawl(self):
        global interrupted
        interrupted = False
        signal.signal(signal.SIGINT, signal_handler)
        while True:
            # doing things
            # more things
            if interrupted:
                print("Exiting..")
                break

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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