Example 5-12. PP4E\System\Threads\thread-add-random.py
"prints different results on different runs on Windows 7"
import threading, time
count = 0
def adder():
global count
count = count + 1 # update a shared name in global scope
time.sleep(0.5) # threads share object memory and global names
count = count + 1
threads = []
for i in range(100):
thread = threading.Thread(target=adder, args=())
thread.start()
threads.append(thread)
for thread in threads: thread.join()
print(count)
Here, 100 threads are spawned to update the same global scope variable twice (with a
sleep between updates to better interleave their operations). When run on Windows 7
with Python 3.1, different runs produce different results:
C:\...\PP4E\System\Threads> thread-add-random.py
189
C:\...\PP4E\System\Threads> thread-add-random.py
200
C:\...\PP4E\System\Threads> thread-add-random.py
194
C:\...\PP4E\System\Threads> thread-add-random.py
191
This happens because threads overlap arbitrarily in time: statements, even the simple
assignment statements like those here, are not guaranteed to run to completion by
themselves (that is, they are not atomic). As one thread updates the global, it may be
using the partial result of another thread’s work in progress. The net effect is this seem-
ingly random behavior. To make this script work correctly, we need to again use thread
locks to synchronize the updates—when Example 5-13 is run, it always prints 200 as
expected.
Example 5-13. PP4E\System\Threads\thread-add-synch.py
"prints 200 each time, because shared resource access synchronized"
import threading, time
count = 0
def adder(addlock): # shared lock object passed in
Threads | 203