Deadlock und Race Condition

Teil 3: Programmieren für die Multi-Core-CPU

Race Conditions

Race Conditions treten immer dann auf, wenn zwei oder mehrere Threads den gleichen Code ausführen und dabei keinen definierten und stabilen Endzustand erreichen. Ein einfaches Beispiel ist jenes, bei dem ein Zähler inkrementiert wird:

Angenommen eine Klasse weist eine Variable vom Typ private static auf. Die Variable wird für jedes erzeugte Objekt um den Wert eins erhöht (in C++: anzahl++ oder anzahl += 1 in Visual Basic). Der Compiler wird dieses Statement in drei CPU-Instruktionen umsetzen: In einen Ladebefehl des Werts in das Register, der Erhöhung des Registers um Eins und einem Zurückspeichern des Registerwertes in die Variable.

Wenn nun in einer multithreaded Applikation ein Thread den Zähler geladen und inkrementiert hat, dann aber durch einen anderen Thread verdrängt wird, bevor er den inkrementierten Zähler in die Variable zurück schreibt, so ist der Wert in der Speicherstelle veraltet und fehlerhaft. Race Conditions treten auch auf, wenn die Reihenfolgen der Operation nicht eingehalten werden. Im Beispiel aus dem ersten Teil dieser Reihe mit der Summenbildung (Sum = C + Z) muss sichergestellt werden, dass die Summenbildung erst dann ausgeführt wird, nachdem die beiden vorherigen Einzelwerte ermittelt wurden. Zwar wird man derlei arithmetische Operationen kaum als unterschiedliche Tasks implementieren, doch das Prinzip gilt natürlich auch dann, wenn statt der Addition (C = A + B) oder Multiplikation (Z = X * Y) Methodenaufrufe stehen, die weitaus komplexere Abläufe umfassen.

Um Fehler dieser Art zu vermeiden, liefern die Hersteller entsprechende Vorkehrungen und Funktionen in ihren Entwicklungswerkzeugen. Race Conditions werden beispielsweise durch die Verwendung der Methoden der Interlocked-Klasse und Methoden wie etwa Interlocked.Increment vermieden. Weitere Hinweise zur Synchronisation von Daten zwischen mehreren Threads sind dem folgenden Dokument zu entnehmen: Synchronizing Data for Multithreading.

Race Conditions treten auch auf, wenn die Aktivitäten mehrerer Threads synchronisiert werden müssen. Auch hier gilt es sicherzustellen, dass der Code, der zuerst abgeschlossen sein muss, auch tatsächlich durchlaufen ist, bevor der davon abhängige Thread zur Ausführung gelangt. Im Prinzipiellen gilt es dabei immer darauf zu achten, was passiert, wenn ein Thread durch den Scheduling-Mechanismus unterbrochen wird und nicht zur Ausführung gelangt.