Reader/Writer Sync in c++ wobei Reader nicht/Kaum geblockt werden sollte
-
Hallo Leute,
und zwar folgendes , ich habe eine SpeicherBereich (irgend ein byte Array).
1-Writer Einheit schreibt da zyklisch daten hin eine und N-Reader Einheiten lesen die Daten.
Um Datenkonsistenz zu haben kann ja der Reader und alle Writer das Array "locken". Nun frage ich mich ob es eine Möglichkeit gibt dass die Reader ohne dass sie ggf. von Writer gelockt werden die Daten konsistent Lesen könnte, ohne dass sie geblockt werden wenn sie lesen möchten. Irgendwie mit double buffering?
Oder wie könnte ich das reader blocking möglichs gering halten?
Danke für euere Ideen
-
Vielleicht ist es ein sinnvoller Ansatz, das Array Read-Only zu halten, ohne explizites Locking und vom Writer bei jedem Schreibvorgang ein neues Array erzeugen zu lassen. Die Reader greifen auf das Read-Only Array über dann einen Atomic Pointer zu. Sobald dann das neue Array fertig geschrieben wurde, wird der Pointer einfach ausgetauscht. Der einzige Synchronisationspunkt, wo die Reader potentiell warten müssen ist dann die extrem schnelle atomare Austauschoperation, was nur wenige Takte wären.
Es ist natürlich mehr Arbeit, das Array jedes mal neu zu schreiben, aber je nach Zugriffsmuster könnte sich das in der Summe lohnen, da die Reader während des Schreibens nicht warten müssen.
Die Reader sollten einen Shared Pointer auf das Array halten, so dass ein Array so lange am Leben bleibt, bis der letzte Reader damit fertig ist. Auch sollten sich die Reader jeweils eine eigene Kopie des Pointers erstellen, die dann selbst kein Atomic ist, so dass die eventuell mehrfachen Pointer-Zugriffe innerhalb eines Readers nicht unter dem geringen Overhead leiden, den Atomics mit sich bringen. Auch will man wahrscheinlich nicht, dass der Pointer z.B. mitten in einem Loop über das Array "ausgetauscht" wird, schon von daher machen eigene Kopien Sinn.
Als "Mehrfach Buffering" würde ich einen Ansatz interpretieren, der ähnlich wie oben beschrieben funktioniert, aber die Arrays nicht jedes mal neu erzeugt, sondern nicht mehr benötigte Arrays landen in einem Pool und können wieder verwendet werden. Das ist eventuell dann sinnvoll, wenn deren Konstruktion "teuer" ist. Das kann je nach Use Case sogar schon die Reservierung des Speichers sein (
malloc
hält ja in nicht-spezialisierten Implementierungen ebenfalls ein globales Lock während es einen geeigneten Speicherbereich sucht). "Double Buffering" wäre dann wenn der Pool 2 Arrays enthält, aber da läuft man Gefahr, dass sich die Threads noch immer gegenseitig auf den Füßen stehen - 2 könnten zu wenig sein. Lieber beliebig viele (jedes mal neue Arrays machen - spart das Pool-Management und dessen Synchronisation) oder wenn die Konstruktion zu teuer ist einen Pool, der groß genug ist. Am besten N, so dass jeder Reader theoretisch sein eigenes Array haben kann.
-
@Finnegan sagte in Reader/Writer Sync in c++ wobei Reader nicht/Kaum geblockt werden sollte:
Vielleicht ist es ein sinnvoller Ansatz, das Array Read-Only zu halten, ohne explizites Locking und vom Writer bei jedem Schreibvorgang ein neues Array erzeugen zu lassen. Die Reader greifen auf das Read-Only Array über dann einen Atomic Pointer zu. Sobald dann das neue Array fertig geschrieben wurde, wird der Pointer einfach ausgetauscht. Der einzige Synchronisationspunkt, wo die Reader potentiell warten müssen ist dann die extrem schnelle atomare Austauschoperation, was nur wenige Takte wären.
hmm das hört sich ganz gut, an so in die Richtung habe ich mir das auch vorgestellt.. da ich das alles in C99 umsetze würdest du die pointer(addresse in ein intptr speichern welche dann über bspw. (WinAPI: "InterlockedExchangeAcquire" swappen könnte) oder?
sorry dass ich im c++ gepostet habe;)
-
@SoIntMan sagte in Reader/Writer Sync in c++ wobei Reader nicht/Kaum geblockt werden sollte:
hmm das hört sich ganz gut, an so in die Richtung habe ich mir das auch vorgestellt.. da ich das alles in C99 umsetze würdest du die pointer(addresse in ein intptr speichern welche dann über bspw. (WinAPI: "InterlockedExchangeAcquire" swappen könnte) oder?
sorry dass ich im c++ gepostet habe;)
Ich bin nicht ganz so bewandert in C. Ich würde allerdings C-Standardfunktionen nehmen (soweit ich sehe gibt's aber erst ab C11
<stdatomic.h>
) und ansonsten was der Compiler anbietet. Das ist etwas portabler als Funktionen der Windows-API (falls das für deinen Anwendungsfall überhaupt relevant ist).Dass keine Shared Pointer für die Freigabe der nicht mehr benötigten Arrays direkt zur Verfügung stehen ist allerdings etwas unpraktisch. Du kannst zwar immer neue Arrays erstellen und einen globalen Pointer auf das jeweils aktuelle relativ einfach mit so einer atomaren Exchange-Operation wie
InterlockedExchangeAcquire
austauschen, aber du musst auch wissen, wann nicht mehr benötigte Arrays wieder freigegeben werden können. Da braucht es schon sowas wie ein threadsicheres Reference Counting (ebenfalls mit Atomics). Das ist für so einen Spezialfall schon machbar, aber nicht leicht korrekt hinzubekommen - nicht viel Code, aber Tests und Fehler die man dabei macht treiben denke ich den Aufwand hoch.Vielleicht gibt es sowas ja als Bibliothek (?). Ich habe da gerade keinen Überblick.