Wie lautet der Code des kritischen Abschnitts für eine gemeinsam genutzte Warteschlange, auf die zwei Threads zugreifen?

8

Angenommen, wir haben eine gemeinsam genutzte Warteschlange (implementiert mit einem Array), auf die zwei Threads zugreifen können, eine zum Lesen von Daten und eine andere zum Schreiben von Daten. Jetzt habe ich ein Problem der Synchronisation. Ich implementiere dies mit Win32 API (EnterCriticalSection etc.).

Aber meine Neugier ist, was wird der kritische Abschnitt Code in Enqueue-und Dequeue-Operationen der Warteschlange?

Nur weil zwei Threads eine gemeinsame Ressource verwenden? Warum ich kein Problem sehe, ist dies: Vorder- und Rückseite werden beibehalten, also wenn ReaderThread liest, kann es vom Frontend lesen und wenn WriterThread schreibt, kann es leicht in das hintere Ende schreiben.

Welche möglichen Probleme können auftreten?

    
Rajendra Uppal 27.07.2011, 04:27
quelle

1 Antwort

6

Bei einer Implementierung von Warteschlangen für einen einzelnen Producer / Consumer sind Sperren nicht erforderlich. Legen Sie einfach eine Bedingung fest, bei der der Produzent nicht in die Warteschlange schreiben kann, wenn die Warteschlange voll ist und der Benutzer die Warteschlange nicht lesen kann, wenn sie leer ist. Außerdem schreibt der Producer immer in einen Zeiger tail , der auf den ersten verfügbaren leeren Slot in der Warteschlange verweist, und der Consumer liest von einem head -Zeiger, der den ersten ungelesenen Slot in der Warteschlange darstellt.

Ihr Code kann wie das folgende Codebeispiel aussehen (Hinweis: Ich nehme in einer initialisierten Warteschlange an, dass tail == head ist, und dass beide Pointer deklariert werden volatile , so dass ein optimierender Compiler die Sequenz nicht neu anordnet Operationen innerhalb eines bestimmten Threads: Auf x86 sind aufgrund des starken Speicherkonsistenzmodells für die Architektur keine Speicherbarrieren erforderlich, aber dies würde sich bei anderen Architekturen mit schwächeren Speicherkonsistenzmodellen ändern, bei denen Speicherbarrieren erforderlich wären):

%Vor%

Die Funktion next_slot inkrementiert einfach den Zeiger head oder tail , so dass sie einen Zeiger auf den nächsten Slot im Array zurückgibt und alle Array-Wrap-Around-Funktionen berücksichtigt.

Schließlich garantieren wir die Synchronisation im Einzelproduzenten- / Konsumentenmodell, da wir den tail -Pointer nicht inkrementieren, bis er die Daten in den Slot geschrieben hat, auf den er gezeigt hat, und wir den head -Pointer nicht bis inkrementieren Wir haben die Daten von dem Slot gelesen, auf den sie zeigen. Daher wird ein Aufruf von dequeue erst dann gültig zurückgegeben, wenn mindestens ein Aufruf von enequeue erfolgt ist, und der Zeiger tail überschreibt niemals den Zeiger head wegen der Überprüfung in enqueue . Darüber hinaus inkrementiert nur ein Thread den Zeiger tail , und ein Thread erhöht den Zeiger head , so dass keine Probleme mit einem gemeinsamen Lesen oder Schreiben von oder zu demselben Zeiger auftreten, was zu Synchronisationsproblemen führen würde, die eine Sperre oder erfordern eine Art atomare Operation.

    
Jason 27.07.2011, 04:48
quelle