Skip to content Skip to sidebar Skip to footer

Ending Non-daemon Threads When Shutting Down An Interactive Python Session

Please consider the code below: #! /usr/bin/env python3 import threading import time class MyThread(threading.Thread): def __init__(self): super().__init__()

Solution 1:

Okay I found a way to do this myself.

Looking at the Python source-code, it turns out that at shutdown, Python attempts to join() all non-daemonic threads before completing the shutdown of the process. Which sort-of makes sense, except that this would be much more useful if there were a documented way to signal those threads. Which there isn't.

However, it is possible to re-implement the join() method of one's own class derived from Thread, which can that serve as a way to notify the subthread that it is supposed to shut itself down:

classMyThread(threading.Thread):
    def__init__(self):
        super().__init__()
        self._quit_flag = Falsedef__del__(self):
        print("Bye bye!")
    defrun(self):
        whilenot self._quit_flag:
            print("Thread is alive!",  threading.active_count(), threading.main_thread().is_alive())
            for t in threading.enumerate():
                print(t.is_alive())
            time.sleep(0.500)
    defrequest_quit(self):
        self._quit_flag = Truedefjoin(self):
        self.request_quit()
        super().join()

However, this way of addressing the issue is a pretty much a hack, and it ignores the following passage from the 'threading' module docs (https://docs.python.org/3/library/threading.html):

The Thread class represents an activity that is run in a separate thread of control. There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass. No other methods (except for the constructor) should be overridden in a subclass. In other words, only override the __init__() and run() methods of this class.

It does, however, work beautifully.

Solution 2:

I am not sure if this would be acceptable, but the only way I can see around your problem is to start the interactive console from your script instead of with -i flag. Then you would be able to carry on with your program to do the exit, after you terminate your interactive session:

import threading
import time
from code import InteractiveConsole

defexit_gracefully():
    print("exiting gracefully now")
    global mt
    mt.request_quit()
    mt.join()


classMyThread(threading.Thread):
    def__init__(self):
        super().__init__()
        self._quit_flag = Falsedefrun(self):
        whilenot self._quit_flag:
            print("Thread is alive!")
            time.sleep(0.500)
    defrequest_quit(self):
        self._quit_flag = True

mt = MyThread()
mt.start()
InteractiveConsole(locals=locals()).interact()
exit_gracefully()

This you would execute without -i flag, only as

python3 test.py

It would still give you the interactive console as with python -i, but now the script executes exit_gracefully() after the interactive shell exits.

Solution 3:

I think the way you can do this is to register a cleanup function using atexit and set your thread as a daemon, eg:

#! /usr/bin/env python3import threading
import time
import atexit

classMyThread(threading.Thread):
    def__init__(self):
        super().__init__()

        # this ensures that atexit gets called.
        self.daemon = True
        self._quit_flag = Falsedefrun(self):
        whilenot self._quit_flag:
            print("Thread is alive!")
            time.sleep(0.500)
        print("cleanup can also go here")

    defrequest_quit(self):
        print("cleanup can go here")
        self._quit_flag = True


mt = MyThread()
defcleanup():
    mt.request_quit()
    mt.join()
    print("even more cleanup here, so many options!")

atexit.register(cleanup)
mt.start()

Post a Comment for "Ending Non-daemon Threads When Shutting Down An Interactive Python Session"