Closing all threads with a keyboard interrupt

Question:

What I’m trying to do here is use a keyboard interrupt to exit all ongoing threads in the program. This is a pared down version of my code where the thread is created:

for i in taskDictionary:
    try:
        sleep(60)
        thread = Thread(target = mainModule.executeThread)
        thread.start()
    except KeyboardInterrupt:
        thread.__stop()

The program itself is far more complicated, accounting for tons of different variables that affect the threads and even having the option to launch in a sequential mode, where tasks are not threaded, but instead launched one-by-one so there might be some problems with this small variation I just conjured up.
I have done this in ways that produced 50/50 results. The interrupts would work but the threads would never cleanly exit. Sometimes they would keep going but halt the execution of future threads, some times they would exit with a massive error regarding the interrupt, other times the interrupts would do nothing at all. Last time I ran this the program stopped any future threads from executing but did not stop the current thread.
Is there any way to exit the threads without going into the module the thread is actually executing in?

Asked By: Joe

||

Answers:

A similar question is “How do you kill a thread?”

You create an exit handler in your thread that is controlled by a lock or event object from the threading module. You then simply remove the lock or signal the event object. This informs the thread it should stop processing and exit gracefully. After signaling the thread in your main program, the only thing left to do is to use the thread.join() method in main which will wait for the thread to shut down.

A short example:

import threading
import time

def timed_output(name, delay, run_event):
    while run_event.is_set():
        time.sleep(delay)
        print name,": New Message!"


def main():
    run_event = threading.Event()
    run_event.set()
    d1 = 1
    t1 = threading.Thread(target = timed_output, args = ("bob",d1,run_event))

    d2 = 2
    t2 = threading.Thread(target = timed_output, args = ("paul",d2,run_event))

    t1.start()
    time.sleep(.5)
    t2.start()

    try:
        while 1:
            time.sleep(.1)
    except KeyboardInterrupt:
        print "attempting to close threads. Max wait =",max(d1,d2)
        run_event.clear()
        t1.join()
        t2.join()
        print "threads successfully closed"

if __name__ == '__main__':
    main()

If you REALLY need the functionality of killing a thread, use multiprocessing. It allows you to send SIGTERMs to individual “processes” (it’s also very similar to the threading module). Generally speaking, threading is for when you are IO-bound, and multiprocessing is for when you are truly processor-bound.

Answered By: Paul Seeb

There are a couple of options that don’t require using locks or other signals between threads. One is setting the threads as daemons, which will be killed off automatically when the main thread exits. The other is using processes instead, which have a terminate method you can call from the main process, if you needed to kill them and keep the main program running.

Both of these are especially useful if you have threads blocking on input. While using a timeout and periodically checking the signal would work in most cases without too much overhead, using daemons or processes eliminates the need for any busy loops or significant added complexity.

See the answers to this question for more details on these solutions and discussion on the problem of killing threads.

Answered By: Scott Yeager
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.