I have a script that creates a bunch of threads, runs a program to use the threads to run tasks from a queue, and returns something from each thread. I want to count how many of
Based on Python variable scope error,
I should have put "global successful" under "def foo():".
Oops.
The problem happens because a variable that is assigned inside a function is considered to be local to that function. If you want to modify the value of a variable successfull
, that is created outside a foo
, you need to explicitly inform the interpreter that you are going to work with a global variable inside a function. This can be done in the following way:
def foo():
global successfull
while True:
task=q.get()
#do some work
print task
successful+=1 # triggers an error
q.task_done()
Now the code should work as expected.
successful+=1
is not a thread-safe operation. With multiple threads trying to increment a shared global variable, collisions may happen and successful
will not be incremented properly.
To avoid this error, use a lock:
lock = threading.Lock()
def foo():
global successful
while True:
...
with lock:
successful+=1
Here is some code to demonstrate that x += 1 is not threadsafe:
import threading
lock = threading.Lock()
x = 0
def foo():
global x
for i in xrange(1000000):
# with lock: # Uncomment this to get the right answer
x += 1
threads = [threading.Thread(target=foo), threading.Thread(target=foo)]
for t in threads:
t.daemon = True
t.start()
for t in threads:
t.join()
print(x)
yields:
% test.py
1539065
% test.py
1436487
These results do not agree and are less than the expected 2000000. Uncommenting the lock yields the correct result.