home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Previous Section Next Section

14.4 The threading Module

The threading module is built on top of module thread and supplies multithreading functionality in a more usable 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 (meaning 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 of this chapter, threading supplies the currentThread factory function.

currentThread

currentThread(  )

Returns a Thread object for the calling thread. If the calling thread was not created by module threading, currentThread creates and returns a semi-dummy Thread object with limited functionality.

14.4.1 Thread Objects

A 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 (non-daemon) thread keeps Python alive until the thread terminates. Class Thread exposes the following constructor and methods.

Thread

class Thread(name=None,target=None,args=(  ),kwargs={  })

Always call Thread with named arguments: the number and order of formal arguments may change in the future, but the names of existing arguments are guaranteed to stay. When you instantiate class Thread itself, you should specify target: t.run calls target(*args,**kwargs). When you subclass Thread and override run, you normally don't specify target. In either case, execution doesn't begin until you call t.start( ). name is t's name. If name is None, Thread generates a unique name for t. If a subclass T of Thread overrides _ _init_ _, T._ _init_ _ must call Thread._ _init_ _ on self before any other Thread method.

getName, setName

t.getName(  )
t.setName(name)

getName returns t's name, and setName rebinds t's name. The name string is arbitrary, and a thread's name need not be unique among threads.

isAlive

t.isAlive(  )

Returns True if t is active (i.e., if t.start has executed and t.run has not yet terminated). Otherwise, isAlive returns False.

isDaemon, setDaemon

t.isDaemon(  )
t.setDaemon(daemonic)

isDaemon returns True if t is a daemon (i.e., Python can terminate the whole process even if t is still active—such a termination also terminates t); otherwise isDaemon returns False. Initially, t is a daemon if and only if the thread creating t is a daemon. You can call t.setDaemon only before t.start: it sets t to be a daemon if daemonic is true.

join

t.join(timeout=None)

The calling thread (which must not be t) suspends until t terminates. timeout is covered in the upcoming section Section 14.4.2.1. You can call t.join only after t.start.

run

t.run(  )

run is the method that executes t's main function. Subclasses of Thread often override run. Unless overridden, run calls the target callable passed on t's creation. Do not call t.run directly—calling t.run appropriately is the job of t.start!

start

t.start(  )

start makes t active and arranges for t.run to execute in a separate thread. You must call t.start only once for any given thread object t.

14.4.2 Thread Synchronization Objects

The 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" later in this chapter 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 parameters

Synchronization primitives Condition and Event supply wait methods that accept a timeout argument. A Thread object's join method also accepts a 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 not None, a timeout argument is a floating-point value that indicates an interval of time in seconds (timeout can have a fractional part and so 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 objects

The Lock objects exposed by module threading are the same as those supplied by module thread and covered in "The thread Module" earlier in this chapter. RLock objects supply the same methods as Lock objects. The semantics of an RLock object r are, however, often more convenient. 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 is a good way to ensure the lock is indeed released.

14.4.2.3 Condition objects

A Condition object c wraps a Lock or RLock object L. Class Condition exposes the following constructor and methods.

Condition

class Condition(lock=None)

Condition creates and returns a new Condition object c with the lock L set to lock. If lock is None, L is set to a newly created RLock object.

acquire, release

c.acquire(wait=1)
c.release(  )

These methods call L's corresponding methods. A thread must never call any other method on c unless the thread holds lock L.

notify, notifyAll

c.notify(  )
c.notifyAll(  )

notify wakes up one of the threads waiting on c. The calling thread must hold L before it calls c.notify( ), and notify does not release L. The woken-up thread does not become ready until it can acquire L again. Therefore, the calling thread normally calls release after calling notify. notifyAll is like notify, but wakes up all waiting threads, not just one.

wait

c.wait(timeout=None)

wait releases L, then suspends the calling thread until some other thread calls notify or notifyAll on c. The calling thread must hold L before it calls c.wait( ). timeout is covered earlier in Section 14.4.2.1. After a thread wakes up, either by notification or timeout, the thread becomes ready when it acquires L again. When wait returns, the calling thread always holds L again.

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 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 objects

Event objects let any number of threads suspend and wait. All threads waiting on Event object e become ready when some other thread calls e.set( ). e has a flag recording whether the event happened, 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 are brittle for more general uses, as resetting an event object (i.e., relying on calls to e.clear( )) is quite error-prone. Class Event exposes the following methods.

Event

class Event(  )

Event creates and returns a new Event object e.

clear

e.clear(  )

Sets e's flag to False.

isSet

e.isSet(  )

Returns the value of e's flag, True or False.

set

e.set(  )

Sets e's flag to True. All threads waiting on e, if any, become ready to run.

wait

e.wait(timeout=None)

If e's flag is True, wait returns immediately. Otherwise, wait suspends the calling thread until some other thread calls set. timeout is covered earlier in Section 14.4.2.1.

14.4.2.5 Semaphore objects

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., four printers or twenty sockets), although it's often more robust to use a Queue. A semaphore object s exposes the following methods.

Semaphore

class Semaphore(n=1)

Semaphore creates and returns a semaphore object s with the state set to n.

acquire

s.acquire(wait=True)

When s's state is greater than 0, acquire decrements the state by 1 and returns True. When s's state is 0 and wait is True, acquire suspends the calling thread and waits until some other thread calls s.release. When s's state is 0 and wait is False, acquire immediately returns False.

release

s.release(  )

When s's state is greater than 0 or when the state is 0 but no thread is waiting on s, release increments the state by 1. When s's state is 0 and some thread is waiting on s, release leaves s's state at 0 and wakes up an arbitrary waiting thread. The thread that calls release is not suspended: it remains ready and continues to execute normally.

    Previous Section Next Section