グローバル変数をスレッドで共有するには?
私のPythonのコード例は
from threading import Thread
import time
a = 0 #global variable
def thread1(threadname):
#read variable "a" modify by thread 2
def thread2(threadname):
while 1:
a += 1
time.sleep(1)
thread1 = Thread( target=thread1, args=("Thread-1", ) )
thread2 = Thread( target=thread2, args=("Thread-2", ) )
thread1.join()
thread2.join()
2つのスレッドで1つの変数を共有する方法がわかりません。
aを
thread2のグローバルとして宣言し、その関数にローカルな
a`を変更しないようにすればよいのです。
def thread2(threadname):
global a
while True:
a += 1
time.sleep(1)
スレッド1では、
a`の値を変更しようとしない限り、特別なことをする必要はありません(グローバル変数の影となるローカル変数が作成されてしまいます。
def thread1(threadname):
#global a # Optional if you treat a as read-only
while a < 10:
print a
機能では
a += 1
はコンパイラによって assign to a => Create local variable a
と解釈されますが、これは望んでいるものではありません。ローカルの)a が実際に初期化されていないので、おそらく a not initialized
というエラーで失敗します。
>>> a = 1
>>> def f():
... a += 1
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment
非常に嫌われている、そして正当な理由のある)global
キーワードを使えば、次のようにして欲しいものが得られるかもしれません。
>>> def f():
... global a
... a += 1
...
>>> a
1
>>> f()
>>> a
2
しかし、一般的には、すぐに手に負えなくなるようなグローバル変数の使用は _避けるべきです。特にマルチスレッドのプログラムでは、 a
がいつ変更されたのかを thread1
が知るための同期メカニズムを持っていない場合、このことが当てはまります。要するに、スレッドは 複雑 であり、2つ(またはそれ以上)のスレッドが同じ値を処理するときに、イベントの発生順序を直感的に理解することは期待できません。言語、コンパイラ、OS、プロセッサ...すべてが役割を果たし、速度や実用性、その他の理由で操作の順序を変更することを決定します。
このような場合の適切な方法は、Pythonの共有ツール(lockksや友人)を使用することです。 など)を使うか、あるいは共有する代わりにQueueを使ってデータを通信するのが良いでしょう、例えば以下のように。
from threading import Thread
from queue import Queue
import time
def thread1(threadname, q):
#read variable "a" modify by thread 2
while True:
a = q.get()
if a is None: return # Poison pill
print a
def thread2(threadname, q):
a = 0
for _ in xrange(10):
a += 1
q.put(a)
time.sleep(1)
q.put(None) # Poison pill
queue = Queue()
thread1 = Thread( target=thread1, args=("Thread-1", queue) )
thread2 = Thread( target=thread2, args=("Thread-2", queue) )
thread1.start()
thread2.start()
thread1.join()
thread2.join()
例えば、ランニング。
警告!自宅や職場では絶対にやってはいけません!教室でのみ行います;)
セマフォや共有変数などを使って、ラッシュ状態を回避する。
from threading import Thread
import time
a = 0 # global variable
def thread1(threadname):
global a
for k in range(100):
print("{} {}".format(threadname, a))
time.sleep(0.1)
if k == 5:
a += 100
def thread2(threadname):
global a
for k in range(10):
a += 1
time.sleep(0.2)
thread1 = Thread(target=thread1, args=("Thread-1",))
thread2 = Thread(target=thread2, args=("Thread-2",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
と出力されます。
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 2
Thread-1 3
Thread-1 3
Thread-1 104
Thread-1 104
Thread-1 105
Thread-1 105
Thread-1 106
Thread-1 106
Thread-1 107
Thread-1 107
Thread-1 108
Thread-1 108
Thread-1 109
Thread-1 109
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
タイミングが良ければ、a += 100
の操作はスキップされます。
プロセッサは T a+100
で実行し、104 を取得します。しかし、それを止めて、次のスレッドに飛びます。
ここで、T+1において、古いaの値であるa == 4
を使って、a+1
を実行します。 そこで、5を計算します。
(T+2の時点で)スレッド1にジャンプバックして、メモリに `a=104
を書き込みます。
今度はスレッド2に戻って、時間はT+3で、a=5
をメモリに書き込みます。
ほら!次のプリント命令では、104ではなく5がプリントされます。
これは非常に厄介なバグで、再現して捕まえる必要があります。