简体   繁体   中英

How to stop threads running infinite loops in python?

I've made a program which has a main thread that spawns many other threads by subclassing the threading.Thread class.

Each such child thread runs an infinite while loop, and inside the loop I check a condition. If the condition is true, I make the thread sleep for 1 second using time.sleep(1) and if it's false, then the thread performs some computation.

The program itself works fine and I've achieved what I wanted to do, my only remaining problem is that I seem unable to stop the threads after my work is done. I want the user to be able to kill all the threads by pressing a button or giving a keyboard interrupt like Ctrl+C.

For this I had tried using the signal module and inserted a conditon in the threads' loops that breaks the loop when the main thread catches a signal but it didn't work for some reason. Can anyone please help with this?

EDIT: This is some of the relevant code snippets:

def sighandler(signal,frame):
    BaseThreadClass.stop_flag = True
class BaseThreadClass(threading.Thread):
    stop_flag = False
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self,*args):
        while True:
            if condition:
               time.sleep(1)
            else:
               #do computation and stuff
            if BaseThreadClass.stop_flag:
               #do cleanup
               break

Your basic method does work, but you've still not posted enough code to show the flaw. I added a few lines of code to make it runnable and produced a result like:

$ python3 test.py
thread alive
main alive
thread alive
main alive
^CSignal caught
main alive
thread alive
main alive
main alive
main alive
^CSignal caught
^CSignal caught
main alive
^Z
[2]+  Stopped                 python3 test.py
$ kill %2

The problem demonstrated above involves the signal handler telling all the threads to exit, except the main thread, which still runs and still catches interrupts. The full source of this variant of the sample snippet is:

import threading, signal, time

def sighandler(signal,frame):
    BaseThreadClass.stop_flag = True
    print("Signal caught")

class BaseThreadClass(threading.Thread):
    stop_flag = False
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self,*args):
        while True:
            if True:
               time.sleep(1)
               print("thread alive")
            else:
               #do computation and stuff
               pass
            if BaseThreadClass.stop_flag:
               #do cleanup
               break

signal.signal(signal.SIGINT, sighandler)

t = BaseThreadClass()
t.start()

while True:
    time.sleep(1)
    print("main alive")

The problem here is that the main thread never checks for the quit condition. But as you never posted what the main thread does, nor how the signal handler is activated, or information regarding whether threads may go a long time without checking the quit condition... I still don't know what went wrong in your program. The signal example shown in the library documentation raises an exception in order to divert the main thread.

Signals are a rather low level concept for this task, however. I took the liberty of writing a somewhat more naïve version of the main thread:

try:
    t = BaseThreadClass()
    t.start()
    while True:
        time.sleep(1)
        print("main alive")
except KeyboardInterrupt:
    BaseThreadClass.stop_flag = True
    t.join()

This version catches the exception thrown by the default interrupt handler, signals the thread to stop, and waits for it to do so. It might even be appropriate to change the except clause to a finally , since we could want to clean the threads up on other errors too.

If you want to do this kind of "cooperative" polled-shutdown, you can use a threading.Event to signal:

import threading
import time

def proc1():
    while True:
        print("1") # payload
        time.sleep(1)

        # have we been signalled to stop?
        if not ev1.wait(0): break 

    # do any shutdown etc. here
    print ("T1 exiting")

ev1 = threading.Event()
ev1.set()

thread1 = threading.Thread(target=proc1)
thread1.start()

time.sleep(3)

# signal thread1 to stop
ev1.clear()

But be aware that if the "payload" does something blocking like network or file IO, that op will not be interrupted. You can do those blocking ops with a timeout, but that obviously will complicate your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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