ever execute a print call at the same point in time; the lock ensures mutually exclusive
access to the stdout stream. Hence, the output of this script is similar to that of the
original version, except that standard output text is never mangled by overlapping
prints:
C:\...\PP4E\System\Threads> thread-count-mutex.py
[0] => 0
[1] => 0
[3] => 0
[2] => 0
[4] => 0
[0] => 1
[1] => 1
[3] => 1
[2] => 1
[4] => 1
[0] => 2
[1] => 2
[3] => 2
[4] => 2
[2] => 2
[0] => 3
[1] => 3
[3] => 3
[4] => 3
[2] => 3
[0] => 4
[1] => 4
[3] => 4
[4] => 4
[2] => 4
Main thread exiting.
Though somewhat platform-specific, the order in which the threads check in with their
prints may still be arbitrary from run to run because they execute in parallel (getting
work done in parallel is the whole point of threads, after all); but they no longer collide
in time while printing their text. We’ll see other cases where the lock idiom comes in
to play later in this chapter—it’s a core component of the multithreading model.
Waiting for spawned thread exits
Besides avoiding print collisions, thread module locks are surprisingly useful. They can
form the basis of higher-level synchronization paradigms (e.g., semaphores) and can
be used as general thread communication devices.§ For instance, Example 5-8 uses a
global list of locks to know when all child threads have finished.
§They cannot, however, be used to directly synchronize processes. Since processes are more independent, they
usually require locking mechanisms that are more long-lived and external to programs. Chapter 4’s
os.open call with an open flag of O_EXCL allows scripts to lock and unlock files and so is ideal as a cross-process
locking tool. See also the synchronization tools in the multiprocessing and threading modules and the IPC
section later in this chapter for other general synchronization ideas.
Threads | 195