Могут ли потоки python обращаться к переменным в пространстве имен?
У меня есть script, который создает кучу потоков, запускает программу для использования потоков для запуска задач из очереди и возвращает что-то из каждого потока. Я хочу подсчитать, сколько из них успешно возвращено, поэтому я устанавливаю переменную "success = 0" и увеличиваю ее каждый раз, когда очередь сообщает о завершении задачи.
Тем не менее, я получаю "UnboundLocalError: локальная переменная" успешно ", на которую ссылаются перед назначением"
Что происходит?
Вот пример кода:
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
Ответы
Ответ 1
successful+=1
не является потокобезопасной. Если несколько потоков пытаются увеличить общую глобальную переменную, могут возникнуть столкновения, а successful
не будет увеличиваться правильно.
Чтобы избежать этой ошибки, используйте блокировку:
lock = threading.Lock()
def foo():
global successful
while True:
...
with lock:
successful+=1
Вот какой код, чтобы продемонстрировать, что x + = 1 не является потокобезопасным:
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)
дает:
% test.py
1539065
% test.py
1436487
Эти результаты не согласуются и меньше ожидаемого 2000000. Раскомментирование блокировки дает правильный результат.
Ответ 2
Проблема возникает из-за того, что переменная, назначенная внутри функции, считается локальной для этой функции. Если вы хотите изменить значение переменной successfull
, которая создается за пределами foo
, вам нужно явно сообщить интерпретатору, что вы собираетесь работать с глобальной переменной внутри функции. Это можно сделать следующим образом:
def foo():
global successfull
while True:
task=q.get()
#do some work
print task
successful+=1 # triggers an error
q.task_done()
Теперь код должен работать как ожидалось.
Ответ 3
На основе Ошибка области переменной Python,
Я должен был бы поставить "глобальный успех" под "def foo():".
К сожалению.