![]() |
Ringpuffer Bibliothek veröffentlicht
Hallo,
ich habe soeben eine Bibliothek die ich mal geschrieben hatte als OpenSource auf Github veröffentlicht. Es ist eine generische Ringpufferumsetzung, also ein Ringpuffer den man problemlos für nicht verwaltete Datentypen wie Bytes, Integer, Double usw. aber auch für Klassen wie TMyWorkItem usw. nutzen kann. Das Projekt beinhaltet auch DUnit Tests, wobei sich diese nicht so gut in der IDE ausführen lassen, da auch geworfene Exceptions getestet werden. Das Projekt befindet sich hier: ![]() Es wurde ursprünglich in XE8 begonnen, läuft auch in 10.4 und vermutlich auch in älteren Versionen, wobei deren Nutzer ruhig mal an ein Update denken dürfen ;-) Wer was zum Projekt beitragen möchte gerne: z.B. fehlen noch ein paar Unit Tests (man sieht welche noch leer sind) oder die Kommentare (und damit meine ich auch XMLDOC!) sind noch alle auf Deutsch. Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Warum ist
Delphi-Quellcode:
overload?
TRingbuffer<T>.Create
|
AW: Ringpuffer Bibliothek veröffentlicht
Hallo Uwe,
das weiß ich gerade nicht mehr. Gibt's evtl. verschiedene Konstruktoren? Kannst aber gerne einen Pull Request einreichen. Dann muss ich aber erst lernen wie man die in Git/Github bearbeitet. Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Bin gerade dabei die XMLDOC zu übersetzen...
|
AW: Ringpuffer Bibliothek veröffentlicht
Cool!
|
AW: Ringpuffer Bibliothek veröffentlicht
Der Thread ist schon etwas älter, aber hatte ich ganz aus den Augen verloren,
und bin gerade durch GetIt nochmal draufgestossen worden dass es den RingBuffer gibt. Soweit ich das sehe ist dieser nicht threadsafe. Gibt es da Bestrebungen das mal in diese Richtung zu erweitern ? Ich benutze RingBuffer um Input/Output Ströme zu entkoppeln, die dann mit unterschiedlichen, variablen Datenraten abgearbeitet werden. Da wäre threadsafety-ness recht praktisch. |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
|
AW: Ringpuffer Bibliothek veröffentlicht
Hallo
der ist vermutlich nicht Thread sicher, jedenfalls war das damals kein Design Ziel. Was auch noch damit ist: er erlaubt es auch nicht, dass Daten die noch nicht abgerufen wurden überschrieben werden können. Sprich: wenn er voll ist un du weitere Daten hinzufügen willst gibt's eine Exception. Es gab' mal die Überlegung, dafür ein Flag einzubauen und die Klasse so zu erweitern, dass das wahlweise auch geht, da ich das aber gerade nicht ernsthaft brauche, hab' ich das noch nicht umgesetzt. Arbeite gerade an anderen Projekten... ...bin aber für Beiträge in diese Richtung und auch in RIchtung einer abgeleiteten Klasse die Threadsicherheit hinzufügt offen! Lohn ist natürlich die Erwähnung in den Credits! ;-) Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Es ist dir ja nicht verboten eine CriticalSection, oder sonstwas hinzuzufügen.
Delphi-Quellcode:
System.TMonitor.Enter(Buffer);
Buffer.Add(irgendwas); System.TMonitor.Exit(Buffer);
Delphi-Quellcode:
Und vielleicht kommt ja Emba irgendwann in diesem Jahrhundert mal auf die Idee den Schwachsinn bei TMonitor zu reparieren.
Buffer.Enter;
Buffer.Add(irgendwas); Buffer.Exit; type TRingbuffer<T> = class(Ringbuffer.TRingbuffer<T>) procedure Enter; procedure Exit; end; procedure TRingbuffer<T>.Enter; begin System.TMonitor.Enter(Self); end; > doppelter/missverständlicher Name und umständliche Nutzung (Wenn das eh in TObjekt integriert ist, dann doch bitte richtig) |
AW: Ringpuffer Bibliothek veröffentlicht
Hallo,
was wäre, wenn man in die Ringpuffer Klasse so wie sie jetzt ist, je eine Enter und Leave (Exit als Name würde ich wegen dem Keyword Exit eher nicht bevorzugen) Methode einbaut, die virtuell, leer und inline ist aber an allen relevanten Stellen aufgerufen wird. Wer dann eine Threadsichere Variante will, braucht nur noch von der bisherigen ableiten und Enter und Leave passend überschreiben. Wie wäre das? Grüße Turbo Magic |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Deshalb frage ich ja ob da schon sowas geplant ist und womöglich kommen wird, oder eher nicht. Zitat:
Zitat:
Ich dachte aber eher an eine Klasse die implizit threadsafe ist, also z.B. TRingbuffer<T> und TRingbuffer_Safe<T> So dass man die Klasse ohne Änderung beim Aufrufer 1:1 tauschen könnte. Das separate Enter/Exit hat natürlich auch seine Vorzüge, einer granulareren Steuerung, wenn man das in vielen unterschiedlichen Szenarien einsetzen würde. Eigentlich möchte ich nur die laufenden Basic's Push/Pop/Length usw. absichern, um bedenkenlos Lesen/Schreiben zu können, das wäre doch sehr gut integrierbar. Welche Vorteile hätte ich denn sonst noch von einem externen, separatem Enter/Exit ? Da sehe ich eher die Gefahr mal ein Enter/Exit zu vergessen. Das ist wohl wieder so eine Philosophie-Frage, wo es am Ende zwei Lager gibt :stupid: Oder gibt es da einen klaren, technischen Favoriten ? |
AW: Ringpuffer Bibliothek veröffentlicht
Historisch hab ich sonst ein Leave statt Exit (Exit hier, weil's in TMonitor so heißt)
Implizit ... joar, ich war schreibfaul ... statt du überall vor Ort zu machen, kann man auch in der Ableitung die Methoden ala Add usw. überdecken/überschreiben und dort das Enter/Exit/Leave rein tun. Überschreiben (virtual+override) ist besser, falls man zwar die TRingbuffer_Safe<> verwendet, aber die Variable auf TRingbuffer<> stehen bleibt. Der Vorteil am "Externen" ist, dass man es für ALLE Klassen verwenden kann, ohne Diese erst anpassen zu müssen. TMonitor hat den Vorteil, dass es multiplatform ist (CriticalSection ist ja Windows) und dass es angeblich schneller sein soll. Aber der wirkliche Vorteil ist, dass man keine Variable dafür braucht. (OK, die braucht es immernoch, aber sie ist bereits in allen TObject-Nachfahren integriert) |
AW: Ringpuffer Bibliothek veröffentlicht
Hallo,
ich hab' ja noch keine Antworten auf meine Idee mit den leeren überschreibbaren Methoden bekommen. Falls das aber ein geeigneter Ansatz wäre eine intrinsische Variante (die ich auch bevorzugen möchte) umzusetzen darfst du das gerne einbringen! Mache einen entsprechenden Pull Request oder schicke mir die Unit anderweitig zu. Wichtig ist halt, dass alle existierenden Unit Tests bestanden werden. Die Frage für mich wäre auch, wie man Multithreaded Code Unit testen kann... => Würde mich über deinen Beitrag dazu sehr freuen! Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
siehe mein Edit?
Alles Testen ist schwer. Explizit einige bestimmte Sperrszenarien kann man als Test aufbauen, aber prüfen, ob eine Funktion per se threadsave ist, kann nicht getestet werden, außer man führt es milliardenmal mit unterschiedlichen Timings/Auslastungen aus und hofft man trifft zufällig eine problematische Überschneidung. |
AW: Ringpuffer Bibliothek veröffentlicht
Sorry, habe ich noch nicht wirklich gesehen.
Bin gedanklich gerade bei der Übernahme von Code für die DEC (siehe anderen Post von mir von eben). |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
zu lassen (ist der Compiler schlau genug das bei Nutzung der Basisklasse als "nop" zu erkennen und somit auszulassen?) oder alle public Methoden als Virtual zu deklarieren und in der abgeleiteten Klasse zu überschreiben? Wie sieht es mit dem XMLDOC aus? Muss man den in der abgeleiteten Klasse duplizieren damit er funktioniert? Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Wäre aber natürlich gut, wenn wir hier vorher möglichst einen klaren Architekturfavoritän küren könnten ;-) Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
![]()
Delphi-Quellcode:
Bis bald...
var
FInOutStream: TSynQueue; begin FInOutStream := TSynQueue.Create(TypeInfo(TRawByteStringDynArray)); ... Thomas |
AW: Ringpuffer Bibliothek veröffentlicht
Vielleicht, nur zieht man sich damit nicht jede Menge andere Dinge aus mormot mit rein?
Die Ringpuffer Umsetzung die angesprochen war hat den Charme, dass es eine einzelne Unit ist. |
AW: Ringpuffer Bibliothek veröffentlicht
Jetzt alle relevanten Methoden in Enter/Leave zu klammern halte ich für wenig hilfreich, denn das sind in non-threadsafe Fall immer zwei überflüssige Calls. Davon abgesehen wird das in einigen Fällen auch nicht reichen, z.B. wenn man erwartet, dass ein Peek gefolgt von einem Pop ein konsistentes Ergebnis liefert. Aber auch das property Notify ist mit dem direkten Feldzugriff nicht wirklich threadsicher.
Delphi-Quellcode:
in einer thread-safe Version ableiten läuft konträr zur aktuellen Ableitungsphilosophie mit
TRingBuffer
Delphi-Quellcode:
. Man müsste dann womöglich jede Ableitung in zwei Flavors machen:
TObjectRingbuffer
TRingbuffer<T> -> TThreadsafeRingBuffer<T>Das erscheint mir nicht sonderlich sinnvoll. Ein möglicher Ansatz, der den ursprünglichen Ringbuffer unangetastet lässt, wäre ein Wrapper für den Zugriff - analog zu
Delphi-Quellcode:
:
TThreadList
Delphi-Quellcode:
type
TThreadRingBufferWrapper<T> = class private FLock: TObject; FRingBuffer: TRingBuffer<T>; public constructor Create(ARingBuffer: TRingBuffer<T>); destructor Destroy; override; function LockBuffer: TRingBuffer<T>; procedure UnlockBuffer; inline; end; ... constructor TThreadRingBufferWrapper<T>.Create(ARingBuffer: TRingBuffer<T>); begin inherited Create; FRingBuffer := ARingBuffer; FLock := TObject.Create(); end; destructor TThreadRingBufferWrapper<T>.Destroy; begin LockBuffer; try FRingBuffer.Free; inherited Destroy; finally UnlockBuffer; FLock.Free; end; end; function TThreadRingBufferWrapper<T>.LockBuffer: TRingBuffer<T>; begin TMonitor.Enter(FLock); Result := FRingBuffer; end; procedure TThreadRingBufferWrapper<T>.UnlockBuffer; begin TMonitor.Exit(FLock); end; |
AW: Ringpuffer Bibliothek veröffentlicht
Hallo,
die Idee ist glaube ich gar nicht so schlecht! Wenn die anderen diese auch als sinnvoll ansehen muss die nur noch jemand offiziell einspeisen... ;-) Am besten mit den Unit Tests die man, trotz der Schwierigkeiten Multithreaded Code Unit getestet zu bekommen, umsetzen kann gleich beigesteuert. Am besten in einer neuen Unit Test Klasse/Fixture... Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Schneller, einfacher: OK, aber wieso sollte TCriticalSection nicht multiplattform sein ? ![]() Für asymmetrische Pushs/Pops Szenarios böte sich da auch noch ![]() </OT> |
AW: Ringpuffer Bibliothek veröffentlicht
Ich habe das nochmal etwas verallgemeinert, da der Wrapper ja gar nichts über die zu wrappende Instanz wissen muss. Damit kann das als Wrapper für quasi jede Objekt-Instanz herhalten und man bekommt über LockInstance auch gleich den tatsachlich übergebenen Typ zurück mit allen dort verfügbaren Methoden und Eigenschaften.
Delphi-Quellcode:
type
TThreadWrapper<T: class> = class private FLock: TObject; FInstance: T; FOwnsInstance: Boolean; public constructor Create(AInstance: T; AOwnsInstance: Boolean = True); destructor Destroy; override; function LockInstance: T; procedure UnlockInstance; inline; property OwnsInstance: Boolean read FOwnsInstance write FOwnsInstance; end; ... constructor TThreadWrapper<T>.Create(AInstance: T; AOwnsInstance: Boolean = True); begin inherited Create; FInstance := AInstance; FLock := TObject.Create(); FOwnsInstance := AOwnsInstance; end; destructor TThreadWrapper<T>.Destroy; begin LockInstance; try if OwnsInstance then FInstance.Free; FInstance := nil; inherited Destroy; finally UnlockInstance; FLock.Free; end; end; function TThreadWrapper<T>.LockInstance: T; begin TMonitor.Enter(FLock); Result := FInstance; end; procedure TThreadWrapper<T>.UnlockInstance; begin TMonitor.Exit(FLock); end; |
AW: Ringpuffer Bibliothek veröffentlicht
Jupp, einige Methoden/Property kann man nicht Thread-safe machen.
Außer man lässt solch problematischen Methoden weg. Und falls man sie dennoch benötigt, dann in einer SubKlasse (ala LockBuffer) verstecken, oder in diesen Methoden/Property eine Exception werfen, wenn vorher extern kein Enter/Lock gestartet wurde. Die komplette Klasse muß man da nicht wegsperren, um die einachen Sachen weiterhin einfach/direkt aufrufen zu können. Wenn FInstance immer keine Klasse ist, dann kannst statt FLock auch direkt FInstance verwenden, oder den TThreadWrapper selbst. |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
|
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Allerdings könnten einige Abfrage-Funktionen womöglich mit InterlockedFunktionen abgesichert werden, statt mit dem großen TMonitor Aufwand. Meine aktuelle RingBuffer-Implementierung ist auf der Basis eines TMemoryStreams gemacht weil es dabei um Byte-Ströme geht, mit CriticalSections, damit bin ich eigentlich ganz zufrieden. Da nutze ich InterLocked Variablen als Indexzeiger, um unnötige Locks zu Vermeiden. Ich wollte immer mal checken ob es schnellere, bessere Alternativen gibt. Der RingBuffer mit ThreadWrapper könnte sowas sein, werde ich mal für mich austesten. Dein Vorschlag mit dem universellen TThreadWrapper<T: class> = class macht schon viel Sinn, weil man es dann auch auf viele andere Probleme übertragen kann. Ich dachte bei dem RingBuffer ursprünglich dass es eine sehr Basic-Library oder Collection ist, welche ihre eigentliche Aufgabe aus meiner Sicht möglichst komplett eigenständig abdecken sollte. Das bezog sich aber sehr auf meinen speziellen Anwendungsfall, wo ich Pop/Push möglichst ohne Zusatzaufwand sicher entkoppeln möchte. Durch den Wrapper wäre die Funktionalität und ThreadSicherheit aber auch schön sauber getrennt, und das passt dann noch in zig andere Situationen rein. Und ja, Push/Pop/Peek, etc. sorgen dafür dass es doch nicht so einfach intrinsisch threadsicher werden kann, ich benutze es momentan so, dass wenn was Neues drin ist, dann Lese und Bearbeite es. Ein Peek nutze ich momentan nicht. Das Duo mit dem Wrapper gefällt mir immer besser, wenn ich mal tiefer drüber nachdenke. |
AW: Ringpuffer Bibliothek veröffentlicht
Dann speise es nach deinen Tests bitte bei dem Projekt ein...
...damit auch die Allgemeinheit davon profitiert. Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Im Idealfall sollte ein thread-safer Ringbuffer lock-free sein und nicht einfach stumpf ne CS oder sowas nutzen.
|
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Man muss ja meistens lesen bzw. schreiben und Start bzw. Ende Index aktualisieren. Da das zwei verschiedene Operationen sind, könnte da ja immer jemand "dazwischen" grätschen... |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
|
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Aber selbst dann können sich Fehler ergeben wenn ein Positionszeiger von mehr als einem Thread zur gleichen Zeit gelesen wird, dann würden all diese Threads auf die gleichen Datenzelle lesend oder schreibend zugreifen. Ich kann mir nicht vorstellen wie man das ohne CS lösen kann. |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
![]() Aber das wäre dann sicher schon die nächste Ausbaustufe des RingBuffers. |
AW: Ringpuffer Bibliothek veröffentlicht
Wie gesagt: der Autor der Bibliothek ist offen für Code Beiträge ;-)
|
AW: Ringpuffer Bibliothek veröffentlicht
AFAIK hat die OTL
![]() |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Grüße TurboMagic |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Ich brauche das im Moment um zufällig eingehende TBytes Sendungen mit variabler Länge, von regelmäßigen Bearbeitungen , welche diese TBytes Daten abholen und verarbeiten sicher zu entkoppeln. Also es kann gerade etwas abgeholt werden, wenn zufällig neue Daten reinkommen, das muss abgesichert werden. Das basiert allerdings auf linearen Speicher und ist auf Byte-Stream-Weise Ein- und Ausgabe optimiert, also ist es mit dem universellen Ringbuffer in dem man Alles reinwerfen kann nicht ganz kompatibel. ( Oder zumindest wäre ein direkter Vergleich etwas unfair ). Ich könnte auch einen FIFO-artigen, linearen Speicher dafür nehmen, müsste dann aber verhindern dass der unendlich anwächst, deshalb hatte ich RingBuffer eingesetzt. In meinem Fall wäre das aber sicher auch mit einem längenbegrentzen FIFO möglich, vom RingBuffer erhoffe ich mir allerdings unnötiges Umkopieren/Löschen von Bytes zu verhindern. Ein Ringbuffer muss ja nicht entsorgt werden, sondern der StartPointer (Integer) läuft einfach bis zum EntPointer (Integer), und die alten Daten bleiben einfach unangetastet im Ring, das kostet fast gar nichts. Trotzdem checke ich immer mal wieder wie sich Alternativen dazu verhalten und ob es gute Ideen dazu gibt. |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
|
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Dass der universelle RingBuffer so nicht optimiert werden kann ist klar. Deshalb schreibe ich ja dass ein Vergleich mit Äpfeln und Birnen etwas unfair ist :stupid: |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
![]() Bis bald... Thomas |
AW: Ringpuffer Bibliothek veröffentlicht
Zitat:
Grüße TurboMagic |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:00 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz