How to stop an infinite loop safely in Python?

2020-02-09 09:25发布

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.

I want to be able to somehow stop a while loop but let it finish it's last iteration before it stops.

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.

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

4条回答
狗以群分
2楼-- · 2020-02-09 09:37

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.

查看更多
够拽才男人
3楼-- · 2020-02-09 09:49

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

查看更多
手持菜刀,她持情操
4楼-- · 2020-02-09 09:53

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:

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:

  1. Use this from the command line. In the IDLE console, it'll trample on IDLE's own interrupt handling.

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

  3. 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.

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.

查看更多
beautiful°
5楼-- · 2020-02-09 09:54

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)
查看更多
登录 后发表回答