18

我有一个创建一堆线程的脚本,运行一个程序来使用线程从队列中运行任务,并从每个线程返回一些东西。我想计算其中有多少成功返回,所以我设置了一个变量“successful=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
4

3 回答 3

22
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。取消注释锁定会产生正确的结果。

于 2012-08-02T22:43:14.997 回答
5

问题的发生是因为在函数内部分配的变量被认为是该函数的本地变量。如果要修改在 asuccessfull之外创建的变量的值,则foo需要明确通知解释器您将在函数内使用全局变量。这可以通过以下方式完成:

def foo():
    global successfull
    while True:
        task=q.get()
        #do some work
        print task
        successful+=1 # triggers an error
        q.task_done()

现在代码应该按预期工作。

于 2012-08-02T22:21:47.163 回答
0

基于Python 变量范围错误

我应该把“全球成功”放在“def foo():”下。

哎呀。

于 2012-08-02T22:21:23.730 回答