デッドロック
複数のリソースがスレッド間で共有されている場合、2 つのスレッドがそれぞれリソースの一部を占有し、同時に互いのリソースを待機すると、デッドロックが発生します。デッドロックが発生することはほとんどありませんが、デッドロックが発生すると、アプリケーションが応答を停止する可能性があります。デッドロックの例を見てみましょう:
# encoding: UTF-8 import threading import time class MyThread(threading.Thread): def do1(self): global resA, resB if mutexA.acquire(): msg = self.name+' got resA' print msg if mutexB.acquire(1): msg = self.name+' got resB' print msg mutexB.release() mutexA.release() def do2(self): global resA, resB if mutexB.acquire(): msg = self.name+' got resB' print msg if mutexA.acquire(1): msg = self.name+' got resA' print msg mutexA.release() mutexB.release() def run(self): self.do1() self.do2() resA = 0 resB = 0 mutexA = threading.Lock() mutexB = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
実行結果:
Thread-1 got resA
Thread-1 got resB
Thread-1 got resB
Thread-1 got resA
Thread-2 resA
Thread-2を取得 resB
Thread-2を取得 resB
Thread-2を取得 resA
Thread-3を取得 resA
Thread-3を取得 resB
Thread-3を取得 resB
Thread-3を取得resA
Thread-5 got resA
Thread-5 got resB
Thread-5 got resB
Thread-4 got resA
この時点でプロセスは終了しました。
リエントラントロック
より単純なデッドロック状況は、スレッドが同じリソースを「反復」してリクエストする場合であり、これが直接デッドロックを引き起こします:
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.acquire() mutex.release() mutex.release() num = 0 mutex = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
同じスレッド内の同じリソースに対する複数のリクエストをサポートするため, Python は、threading.RLock という「再入可能なロック」を提供します。 RLock は内部でロックとカウンター変数を維持するため、リソースが複数回必要になる可能性があるため、カウンターは取得回数を記録します。スレッドのすべての取得が解放されるまで、他のスレッドはリソースを取得できます。上記の例では、Lock の代わりに RLock を使用すると、デッドロックは発生しません:
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.acquire() mutex.release() mutex.release() num = 0 mutex = threading.RLock() def test(): for i in range(5): t = MyThread() t.start() if __name__ == '__main__': test()
実行結果:
Thread-1 set num to 1
Thread-3 set num to 2
Thread-2番号を 3 に設定します
スレッド 5 番号を 4 に設定します
スレッド 4 番号を 5 に設定します