Passing in the lock to threaded functions as an argument instead of referencing it in the
global scope might be more coherent, too. When passed in, all threads reference the
same object, because they are all part of the same process. Really, the process’s object
memory is shared memory for threads, regardless of how objects in that shared memory
are referenced (whether through global scope variables, passed argument names, object
attributes, or another way).
And while we’re at it, the with statement can be used to ensure thread operations around
a nested block of code, much like its use to ensure file closure in the prior chapter. The
thread lock’s context manager acquires the lock on with statement entry and releases
it on statement exit regardless of exception outcomes. The net effect is to save one line
of code, but also to guarantee lock release when exceptions are possible. Exam-
ple 5-10 adds all these coding alternatives to our threaded counter script.
Example 5-10. PP4E\System\Threads\thread-count-wait3.py
"""
passed in mutex object shared by all threads instead of globals;
use with context manager statement for auto acquire/release;
sleep calls added to avoid busy loops and simulate real work
"""
import _thread as thread, time
stdoutmutex = thread.allocate_lock()
numthreads = 5
exitmutexes = [thread.allocate_lock() for i in range(numthreads)]
def counter(myId, count, mutex): # shared object passed in
for i in range(count):
time.sleep(1 / (myId+1)) # diff fractions of second
with mutex: # auto acquire/release: with
print('[%s] => %s' % (myId, i))
exitmutexes[myId].acquire() # global: signal main thread
for i in range(numthreads):
thread.start_new_thread(counter, (i, 5, stdoutmutex))
while not all(mutex.locked() for mutex in exitmutexes): time.sleep(0.25)
print('Main thread exiting.')
When run, the different sleep times per thread make them run more independently:
C:\...\PP4E\System\Threads> thread-count-wait3.py
[4] => 0
[3] => 0
[2] => 0
[4] => 1
[1] => 0
[3] => 1
[4] => 2
[2] => 1
[3] => 2
[4] => 3
198 | Chapter 5: Parallel System Tools