Parallelzugriff auf Daten

Teil 4: Programmieren für Multi-Core-Prozessoren

Parallelverarbeitung mit Multi-Core-Prozessoren kann die Leistung deutlich erhöhen. Aber der Parallelzugriff auf Daten birgt auch Gefahren, die in nicht reproduzierbaren Fehlern resultieren. TecChannel zeigt Ihnen, wie Sie die Gefahren der Multi-Core-Programmierung mit Parallelverarbeitung umgehen.

Die Leistung von Computersystemen steigt seit Jahren kontinuierlich an. Dies betrifft nahezu alle Komponenten der Rechner. Am deutlichsten ist der Leistungsgewinn bei den Prozessoren, dem Arbeitsspeicher und den Festplatten zu verfolgen. Insbesondere die Prozessoren haben nicht nur dank höherer Taktung sondern vor allem durch die Multi-Core-Technologie massiv an Leistungsfähigkeit zugelegt. Einzelne Anwendungen profitieren aber nur von Multi-Core, wenn sie entsprechend programmiert sind.

In dieser mehrteiligen Reihe zur Programmierung für Multi-Core-Prozessoren führen wir Sie nach und nach durch die Besonderheiten der Multi-Core-Programmierung. Angefangen bei den wichtigsten Grundlagen der verschiedenen Parallelisierungs-Verfahren in Prozessoren im ersten Teil. Der zweite Teil drehte sich um die notwendige Modularisierung in Applikationen und das .Net-Framework. Im dritten Teil ging es um Deadlocks, Race Conditions und Zugriffe auf die .Net-Thread-Implementation. Dieser vierte Teil behandelt eine Reihe von Problemen, die beim Parallelzugriff auf gemeinsame Daten auftauchen können, und welche Methoden Sie anwenden, um konsistente Daten zu gewährleisten.

Parallelzugriff auf Daten

Sobald Code parallel auf gemeinsamen Daten ausgeführt wird, sind diese Daten genau zu beobachten. Paralleles Lesen von Daten stellt in der Regel kein Problem dar, aber bei parallelen Schreibzugriffen müssen entsprechende Vorkehrungen getroffen werden.

Das gilt auch für Operationen, die erst einen Lese- und dann einen Update-Zugriff ausführen wollen. Dies tritt beispielsweise bereits dann auf, wenn in einem Objekt ein Zähler mitgeführt wird, der pro Aufruf des Objektes und seiner Methoden inkrementiert wird. Bei parallel ausgeführten Threads können hier Konflikte auftreten:

  • Der Zähler hat den Wert 5

  • Thread A und B lesen gleichzeitig den Zähler aus, arbeiten also mit dem Wert 5

  • Beide Threads inkrementieren den Wert (arbeiten also mit dem Wert 6)

  • Beide Threads schreiben den Wert in den Zähler zurück, der nun 6 enthält, obwohl er eigentlich den Wert 7 haben müsste.

Um solche Situationen zu umgehen und die parallelen Zugriffe zu koordinieren, werden exklusive Locksperren (Zugriffssperren) verwendet. Das .Net-Framework stellt dazu die Klasse ReaderWriterLock bereit. Diese regelt den Zugriff von Threads auf gemeinsam genutzte Ressourcen und erlaubt mehrere parallele Lesezugriffe, aber nur einen Schreibzugriff. Die Funktionsweise und der Nutzen von Locks orientieren sich an denen der Semaphore.