Deadlock
Wenn mehrere Ressourcen von Threads gemeinsam genutzt werden und zwei Threads jeweils einen Teil der Ressourcen belegen und gleichzeitig auf die Ressourcen des anderen warten, kommt es zu einem Deadlock. Obwohl Deadlocks selten auftreten, können sie, wenn sie auftreten, dazu führen, dass die Anwendung nicht mehr reagiert. Schauen wir uns ein Beispiel für einen Deadlock an:
# 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()
Ausführungsergebnis:
Thread-1 hat resA
Thread-1 hat resB
Thread-1 hat resB
Thread-1 hat resA
Thread-2 hat resA
Thread-2 hat resB
Thread-2 hat resB
Thread-2 hat resA
Thread-3 hat resA
Thread-3 hat resB
Thread-3 hat resB
Thread-3 hat resA
Thread-5 hat resA
Thread-5 hat resB
Thread-5 hat resB
Thread-4 hat resA
Der Prozess ist zu diesem Zeitpunkt abgebrochen.
Wiedereintrittssperre
Eine einfachere Deadlock-Situation liegt vor, wenn ein Thread iteriert, um dieselbe Ressource anzufordern, was direkt zu einem Deadlock führt:
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()
Um mehrere Anfragen für dieselbe Ressource im selben Thread zu unterstützen, bietet Python eine „Wiedereintrittssperre“: threading.RLock. RLock verwaltet intern eine Sperre und eine Zählervariable. Der Zähler zeichnet die Anzahl der Erfassungen auf, sodass die Ressource mehrmals benötigt werden kann. Bis alle Acquires eines Threads freigegeben sind, können andere Threads Ressourcen abrufen. Wenn im obigen Beispiel RLock anstelle von Lock verwendet wird, tritt kein Deadlock auf:
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()
Ausführungsergebnis:
Thread-1 set num auf 1
Thread-3 setzt Num auf 2
Thread-2 setzt Num auf 3
Thread-5 setzt Num auf 4
Thread-4 setze num auf 5