14.4. The threading ModuleThe threading module is built on top of module thread and supplies multithreading functionality in a more usable, higher-level form. The general approach of threading is similar to that of Java, but locks and conditions are modeled as separate objects (in Java, such functionality is part of every object), and threads cannot be directly controlled from the outside (which means there are no priorities, groups, destruction, or stopping). All methods of objects supplied by threading are atomic. threading provides numerous classes for dealing with threads, including Thread, Condition, Event, RLock, and Semaphore. Besides factory functions for the classes detailed in the following sections, threading supplies the currentThread factory function.
14.4.1. Thread ObjectsA THRead object t models a thread. You can pass t's main function as an argument when you create t, or you can subclass Thread and override the run method (you may also override _ _init_ _ but should not override other methods). t is not ready to run when you create it; to make t ready (active), call t.start( ). Once t is active, it terminates when its main function ends, either normally or by propagating an exception. A Thread t can be a daemon, meaning that Python can terminate even if t is still active, while a normal (nondaemon) thread keeps Python alive until the thread terminates. Class Thread exposes the following constructor and methods.
14.4.2. Thread Synchronization ObjectsThe tHReading module supplies several synchronization primitives, which are objects that let threads communicate and coordinate. Each primitive has specialized uses. However, as long as you avoid global variables that several threads access, Queue can often provide all the coordination you need. "Threaded Program Architecture" on page 350 shows how to use Queue objects to give your multithreaded programs simple and effective architectures, often without needing any synchronization primitives. 14.4.2.1. Timeout parametersSynchronization primitives Condition and Event supply wait methods that accept an optional timeout argument. A Thread object's join method also accepts an optional timeout argument. A timeout argument can be None (the default) to obtain normal blocking behavior (the calling thread suspends and waits until the desired condition is met). If it is not None, a timeout argument is a floating-point value that indicates an interval of time in seconds (timeout can have a fractional part, so it can indicate any time interval, even a very short one). If timeout seconds elapse, the calling thread becomes ready again, even if the desired condition has not been met. timeout lets you design systems that are able to overcome occasional anomalies in one or a few threads, and thus are more robust. However, using timeout may also make your program slower. 14.4.2.2. Lock and RLock objectsThe Lock objects exposed by module threading are the same as those supplied by module tHRead and covered in "The thread Module" on page 341. RLock objects supply the same methods as Lock objects. The semantics of an RLock object r are, however, often more convenient. An RLock is a "re-entrant" lock, meaning that when r is locked, it keeps track of the owning thread (i.e., the thread that locked it). The owning thread can call r.acquire again without blocking; r just increments an internal count. In a similar situation involving a Lock object, the thread would block forever (until the lock is released by some other thread). An RLock object r is unlocked only when release has been called as many times as acquire. Only the thread owning r should call r.release. An RLock is useful to ensure exclusive access to an object when the object's methods call each other; each method can acquire at the start, and release at the end, the same RLock instance. TRy/finally (covered in "try/finally" on page 123) is a good way to ensure the lock is indeed released (in Python 2.5, the new with statement, covered in "The with statement" on page 125, is generally at least as good). 14.4.2.3. Condition objectsA Condition object c wraps a Lock or RLock object L. Class Condition exposes the following constructor and methods.
In typical use, a Condition object c regulates access to some global state s that is shared between threads. When a thread needs to wait for s to change, the thread loops as follows: c.acquire( ) while not is_ok_state(s): c.wait( ) do_some_work_using_state(s) c.release( ) Meanwhile, each thread that modifies s calls notify (or notifyAll if it needs to wake up all waiting threads, not just one) each time s changes: c.acquire( ) do_something_that_modifies_state(s) c.notify( ) # or, c.notifyAll( ) c.release( ) As you can see, you always need to acquire and release c around each use of c's methods, which makes using Condition somewhat error-prone. 14.4.2.4. Event objectsEvent objects let any number of threads suspend and wait. All threads waiting on Event object e become ready when any other thread calls e.set( ). e has a flag that records whether the event happened; it is initially False when e is created. Event is thus a bit like a simplified Condition. Event objects are useful to signal one-shot changes, but brittle for more general use; in particular, relying on calls to e.clear( ) is error-prone. Class Event exposes the following methods.
14.4.2.5. Semaphore objectsSemaphores (also known as counting semaphores) are a generalization of locks. The state of a Lock can be seen as true or False; the state of a Semaphore s is a number between 0 and some n set when s is created. Semaphores can be useful to manage a fixed pool of resources (e.g., 4 printers or 20 sockets), although it's often more robust to use Queues for such purposes.
14.4.3. Thread Local StorageIn Python 2.4, module threading supplies a class local, which threads can use to obtain thread-local storage (TLS), also known as per-thread data. An instance L of local has arbitrary named attributes that you can set and get, and stores them in a dictionary L._ _dict_ _ that you can also access. L is fully thread-safe, meaning there is no problem if multiple threads simultaneously set and get attributes on L. Most important, each thread that accesses L sees a completely disjoint set of attributes, and any changes made in one thread have no effect in other threads. For example: import threading L = threading.local( ) print 'in main thread, setting zop to 42' L.zop = 42 def targ( ): print 'in subthread, setting zop to 23' L.zop = 23 print 'in subthread, zop is now', L.zop t = threading.Thread(target=targ) t.start( ) t.join( ) print 'in main thread, zop is now', L.zop # emits: # in main thread, setting zop to 42 # in subthread, setting zop to 23 # in subthread, zop is now 23 # in main thread, zop is now 42 TLS makes it easier for you to write code meant to run in multiple threads, since you can use the same namespace (an instance of threading.local) in multiple threads without the separate threads interfering with each other. |