質問です
前のセクションの例では、各スレッドは互いに独立しており、相互に関係はありません。ここで例を想定します。グローバル カウント num があり、各スレッドはこのグローバル カウントを取得し、num に基づいて何らかの処理を実行し、その後 num に 1 を加算します。次のようなコードを書くのは簡単です:
# encoding: UTF-8 import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) num = num+1 msg = self.name+' set num to '+str(num) print msg num = 0 def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
しかし、実行結果は正しくありません:
Thread-5 set num to 2
Thread-3 set num to 3
Thread-2 set num to 5
Thread-1 set num to 5
Thread-4 set num to 4
問題の理由は、同じリソースへの複数のスレッドのアクセスが制御されていないため、データが損傷し、スレッド操作の結果は予測できません。この現象は「スレッドアンセーフ」と呼ばれます。
ミューテックス ロックの同期
上記の例は、マルチスレッド プログラミングの最も一般的な問題、つまりデータ共有につながります。ある共有データを複数のスレッドで変更する場合、同期制御が必要となります。
スレッド同期により、複数のスレッドが競合するリソースに安全にアクセスできるようになります。最も簡単な同期メカニズムは、ミューテックス ロックを導入することです。ミューテックスは、ロック/ロック解除という状態をリソースに導入します。スレッドが共有データを変更したい場合は、まずそのリソースをロックする必要があり、その時点でリソースのステータスは「ロック」されており、スレッドがリソースを解放してリソースのステータスを「」に変更するまで、他のスレッドはそれを変更できません。ロックが解除されました」と表示された場合、他のスレッドはリソースを再度ロックできます。ミューテックス ロックは、一度に 1 つのスレッドだけが書き込み操作を実行することを保証するため、マルチスレッド状況でのデータの正確性が保証されます。
Lock クラスはスレッド モジュールで定義されており、ロックを簡単に処理できます:
#Create lock
mutex = threading.Lock()
#Lock
mutex.acquire([timeout])
#Release
mutex.release()
このうち、lockメソッドacquireはタイムアウト付きのパラメータtimeoutをオプションで持つことができます。タイムアウトが設定されている場合、戻り値を使用してタイムアウト後にロックが取得されたかどうかを判断できるため、他の処理を実行できます。
ミューテックスを使用して上記の例を実装するコードは次のとおりです:
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) if mutex.acquire(1): num = num+1 msg = self.name+' set num to '+str(num) print msg mutex.release() num = 0 mutex = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
実行結果:
Thread-3 set num to 1
Thread-4 set num to 2
Thread-5 set num to 3
Thread-2 set num to 4
Thread-1 set num to 5
ミューテックス ロックを追加した後の実行結果が期待どおりであることがわかります。
同期ブロッキング
スレッドがロックのacquire()メソッドを呼び出してロックを取得すると、ロックは「ロック」状態になります。一度に 1 つのスレッドだけがロックを取得できます。このときに別のスレッドがロックを取得しようとすると、そのスレッドは「ブロック」されます。これを「同期ブロック」と呼びます (マルチスレッドの基本概念を参照)。
ロックを所有するスレッドがロックの release() メソッドを呼び出してロックを解放するまで、ロックは「ロック解除」状態になります。スレッド スケジューラは、同期ブロッキング状態にあるスレッドの 1 つを選択してロックを取得し、そのスレッドを実行状態にします。
ミューテックス ロックの最も基本的な内容は次のとおりです。次のセクションでは、リエントラント ロック (RLock) とデッドロックの問題について説明します。