Recipe9.2.Terminating a Thread


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.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net