Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can python threads access variables in the namespace?

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
like image 646
Oliver Avatar asked Aug 02 '12 22:08

Oliver


People also ask

Can threads share variables Python?

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.

Can Python threads access global 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.

Do threads have access to global variables?

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.

Are variables in Python thread safe?

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.


1 Answers

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.

like image 80
unutbu Avatar answered Oct 10 '22 13:10

unutbu