How to stop an infinite loop safely in Python?

后端 未结 4 1964
长发绾君心
长发绾君心 2020-12-09 11:00

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

相关标签:
4条回答
  • 2020-12-09 11:04

    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

    0 讨论(0)
  • 2020-12-09 11:05

    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.

    0 讨论(0)
  • 2020-12-09 11:05

    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)
    
    0 讨论(0)
  • 2020-12-09 11:21

    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.

    0 讨论(0)
提交回复
热议问题