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 these returned successfully, so I set a variable "successful=0" and increment it every time the queue reports a task completed successfully.
However, I'm getting "UnboundLocalError: local variable 'successful' referenced before assignment"
What's going on?
Here's some example code:
successful = 0
q = Queue(200)
for i in range(100):
t=Thread(target=foo)
t.daemon=True
t.start()
def foo():
while True:
task=q.get()
#do some work
print task
successful+=1 # triggers an error
q.task_done()
for i in range(100):
q.put("Foo")
q.join()
print successful
As mentioned earlier, each thread shares the same memory space. That is, the variables in the program are shared by all the threads and cannot be accessed the way you would normally access a variable.
Since threads live in the same memory space, they have access to the same (public) data. Thus, you can for example simply use a global variable to which all threads have read and write access.
But to answer your question, any thread can access any global variable currently in scope. There is no notion of passing variables to a thread. It has a single global variable with a getter and setter function, any thread can call either the getter or setter at any time.
Local variables and parameters are always thread-safe. Instance variables, class variables, and global variables may not be thread-safe (but they might be). Nevertheless, threads and shared variables can be useful.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With