Recipe 9.2. Terminating a Thread
Credit: Doug Fort
Problem
You
must terminate a thread from the outside, but Python
doesn't let one thread just brutally kill another,
so you need to use a suitable controlled-termination idiom.
Solution
A frequently asked question is: How do I kill a thread? The answer
is: You don't. Instead, you kindly ask it to go
away. Each thread must periodically check whether
it's been asked to go away and then comply
(typically after some kind of cleanup). Here is an example:
import threading
class TestThread(threading.Thread):
def _ _init_ _(self, name='TestThread'):
"" constructor, setting initial variables ""
self._stopevent = threading.Event( )
self._sleepperiod = 1.0
threading.Thread._ _init_ _(self, name=name)
def run(self):
"" main control loop ""
print "%s starts" % (self.getName( ),)
count = 0
while not self._stopevent.isSet( ):
count += 1
print "loop %d" % (count,)
self._stopevent.wait(self._sleepperiod)
print "%s ends" % (self.getName( ),)
def join(self, timeout=None):
"" Stop the thread and wait for it to end. ""
self._stopevent.set( )
threading.Thread.join(self, timeout)
if _ _name_ _ == "_ _main_ _":
testthread = TestThread( )
testthread.start( )
import time
time.sleep(5.0)
testthread.join( )
Discussion
You often want to exert some control on a thread from the outside,
but the ability to kill a thread is, well, overkill. Python
doesn't give you this ability, and thus forces you
to design your thread systems more carefully. This recipe is based on
the idea of a thread whose main function uses a loop. Periodically,
the loop checks if a tHReading.Event object has
been set. If so, the thread terminates; otherwise, it waits for the
object.
The
TestThread class in this recipe also overrides
threading.Thread's
join method. Normally, join
waits only for a certain thread to terminate (for up to a specified
amount of time, if any) without doing anything to
cause that termination. In this recipe, however,
join is overridden to set the stop event object
before delegating the rest of its operation to the normal (base
class) join method. Therefore, in this recipe, the
join call is guaranteed to terminate the target
thread within a short amount of time.You can use the recipe's central idea (a loop
periodically checking a threading.Event to
determine whether it must terminate) in several other, slightly
different ways. The Event's
wait method can let you pause the target thread.
You can also expose the Event, letting controller
code set it and then go on its merry way without
bothering to join the thread, knowing the thread
will terminate in a short amount of time. Once the event is exposed,
you may choose to use the same event to request the termination of
more than one threadfor example, all threads in a certain
thread pool might stop when one event object they all share is set.
The simplicity of this recipe provides the modest amount of control I
need, with no headaches, so I haven't pursued the
more sophisticated (and complicated) ideas.Python also lets you terminate a thread in another way: by raising an
exception in that thread. This
"rougher" approach also has its
limits: it cannot interrupt a blocking call to the operating system,
and it could fail to work if the thread you want to terminate is
executing a TRy clause whose
except clauses are too broad. Despite its limits,
this approach can still sometimes be useful, when
you're essentially writing a debugger: that is, when
you cannot count on the code executing in the target thread to be
well written, but you can hope the code is not written in an utterly
disastrous way. The normal way to make use of this functionality is
by running the possibly-buggy code in the main thread, after spawning
a separate monitoring thread to keep an eye on things. If the
monitoring thread decides the time has come to terminate the code
that is currently running in the main thread, the monitoring thread
can call thread.interrupt_main, passing as the
argument the desired exception class.
Once in a blue moon, the debugger
you're writing cannot run the possibly-buggy code in
the process' main thread, typically because that
thread is required for other uses by some other framework you depend
on, such as your GUI code. To support such remote eventualities, the
Python interpreter has a function that can raise an exception in any
thread, given the target thread's ID. However, this
specialized functionality is intended for a tiny subset of that tiny
subset of Python applications that are debuggers. To avoid tempting
all other Python programmers (well over 99.9%) into misusing this
approach for any other case of thread termination, the function is
not directly callable from Python code: rather, the function is only
exposed as a part of Python's C API. This special
function's name is
PyThreadState_SetAsyncExc, and the
function's two arguments are the target
thread's ID and the class of the desired exception.
If you are writing a Python debugger with such peculiar needs, no
doubt you already have, as part of your code, at least one C-coded
Python extension module that supplies to your higher-level Python
code other tidbits of peculiar, low-level functionality. Just add to
your C code, a Python-callable function that in turn calls
PyThreadState_SetAsyncExc, and your debugger will
gain this peculiar but useful functionality.
See Also
Documentation of the standard library module
threading in the Library
Reference and Python in a
Nutshell.