Position der Frage
Im Beispiel im vorherigen Abschnitt ist jeder Thread unabhängig voneinander und hat keine Beziehung zueinander. Nehmen wir nun ein Beispiel an: Es gibt eine globale Anzahl num, jeder Thread erhält diese globale Anzahl, führt einige Verarbeitungen basierend auf num durch und addiert dann 1 zu num. Es ist einfach, Code wie diesen zu schreiben:
# 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()
, aber das Laufergebnis ist falsch:
Thread-5 set num to 2
Thread-3 hat die Nummer auf 3 gesetzt
Thread-2 hat die Nummer auf 5 gesetzt
Thread-1 hat die Nummer auf 5 gesetzt
Thread-4 hat die Nummer auf 4 gesetzt
Der Grund für das Problem liegt darin, dass der Zugriff mehrerer Threads auf dieselbe Ressource nicht kontrolliert wird, was zu Datenschäden führt und die Ergebnisse des Thread-Betriebs unvorhersehbar macht. Dieses Phänomen wird als „Thread unsicher“ bezeichnet.
Mutex-Sperrsynchronisation
Das obige Beispiel führt zum häufigsten Problem der Multithread-Programmierung: der Datenfreigabe. Wenn mehrere Threads bestimmte gemeinsam genutzte Daten ändern, ist eine Synchronisationssteuerung erforderlich.
Thread-Synchronisierung kann sicherstellen, dass mehrere Threads sicher auf konkurrierende Ressourcen zugreifen. Der einfachste Synchronisierungsmechanismus besteht darin, eine Mutex-Sperre einzuführen. Ein Mutex führt einen Zustand für eine Ressource ein: gesperrt/entsperrt. Wenn ein Thread gemeinsam genutzte Daten ändern möchte, muss er diese zunächst sperren. Zu diesem Zeitpunkt ist der Status der Ressource „gesperrt“ und andere Threads können ihn nicht ändern, bis der Thread die Ressource freigibt und den Status der Ressource in „sperrt“. entsperrt“, andere Threads können die Ressource wieder sperren. Die Mutex-Sperre stellt sicher, dass jeweils nur ein Thread Schreibvorgänge ausführt und stellt so die Korrektheit der Daten in Multithread-Situationen sicher.
Die Lock-Klasse ist im Threading-Modul definiert, das das Sperren bequem handhaben kann:
#Create lock
mutex = threading.Lock()
#Lock
mutex.acquire([timeout])
#Release
mutex.release()
Daunter kann die Sperrmethode „acquire“ vorhanden sein ein Timeout-Zeitraum. Optionaler Parameter timeout. Wenn eine Zeitüberschreitung festgelegt ist, kann der Rückgabewert verwendet werden, um zu bestimmen, ob die Sperre nach der Zeitüberschreitung erhalten wird, sodass eine andere Verarbeitung durchgeführt werden kann.
Der Code zum Implementieren des obigen Beispiels mithilfe eines Mutex lautet wie folgt:
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()
Laufergebnis:
Thread-3 setze num auf 1
Thread-4 setze num auf 2
Thread-5 setze num auf 3
Thread-2 setze num auf 4
Thread -1 num auf 5 setzen
Sie können sehen, dass die laufenden Ergebnisse nach dem Hinzufügen der Mutex-Sperre den Erwartungen entsprechen.
Synchronisierte Blockierung
Wenn ein Thread die acquire()-Methode der Sperre aufruft, um die Sperre zu erhalten, wechselt die Sperre in den Status „gesperrt“. Es kann jeweils nur ein Thread die Sperre erhalten. Wenn zu diesem Zeitpunkt ein anderer Thread versucht, die Sperre zu erhalten, wird der Thread „blockiert“, was als „synchrones Blockieren“ bezeichnet wird (siehe Grundkonzept von Multithreading).
Bis der Thread, der die Sperre besitzt, die Methode release() der Sperre aufruft, um die Sperre freizugeben, wechselt die Sperre in den Status „entsperrt“. Der Thread-Scheduler wählt einen der Threads im synchronen Blockierungszustand aus, um die Sperre zu erhalten, und versetzt den Thread in den laufenden Zustand.
Dies sind die grundlegendsten Inhalte von Mutex-Sperren. Im nächsten Abschnitt werden die Probleme mit Wiedereintrittssperren (RLock) und Deadlocks erläutert.