18

一連のスレッドを作成し、プログラムを実行してスレッドを使用してキューからタスクを実行し、各スレッドから何かを返すスクリプトがあります。これらのうち正常に返された数をカウントしたいので、変数「successful = 0」を設定し、キューがタスクが正常に完了したことを報告するたびにそれをインクリメントします。

ただし、「UnboundLocalError:ローカル変数'successful'が割り当て前に参照されています」というメッセージが表示されます。

どうしたの?

次にいくつかのサンプルコードを示します。

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

この問題は、関数内で割り当てられた変数がその関数に対してローカルであると見なされるために発生します。successfullの外部で作成された変数の値を変更する場合は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変数スコープエラーに基づく、

「deffoo():」の下に「globalsuccessful」を置くべきでした。

おっと。

于 2012-08-02T22:21:23.730 に答える