![]() |
Daten parallel mithilfe eines Ringbuffers wegspeichern - Cri
Moin zusammen.
Ich möchte eine Funktion (Get_Data) aufrufen, welche eine gewisse Zeit aktiv bleibt (z.B 500 ms) und welche gewisse Daten liefert. Unmittelbar nach derm wiederkehren soll die Funktion wieder aufgerufen werden um die nächste Daten zu liefern. Während dessen (also z.B. in den nächsten 500 ms) sollen die Daten die die Funktion beim letzen mal geliefert hat weggespeichert werden. Um diese zu realisieren habe ich mir einen Ringbuffer geschaffen. Desweiteren habe ich den Get_Data-Funktionsaufruf sowie das Wegspeichern der Daten in einen separaten Thread ausgelagert. Die ganze Geschichte funktioniert soweit einwandfrei. Ich habe jedoch zwei Fragen: 1. Sind die Critical Sections im 'Ring Buffer' sinnvoll bzw. nötig? 2. Ist das Konzept so in Ordnung, oder kann man etwas besser, effizienter der eleganter lösen? Da gehe ich mal starkt von aus, da dies das erste mal ist, das ich sowas geschrieben habe. Für jegliches konstruktives Feedback bin ich sehr dankbar. :thumb: Jetzt zur Implementierung: Erstellung der einzelnen Objekte:
Delphi-Quellcode:
procedure TMainForm.FormCreate(Sender: TObject);
begin ... ... FGetDataThread := TGetDataThread.Create(TRUE); FSaveDataThread := TSaveDataThread.Create(TRUE); FRingBuffer := TRingBuffer.Create; FGetDataThread.RingBuffer := FRingBuffer; FSaveDataThread.RingBuffer := FRingBuffer; WM_GET_DATA_COMPLETE := RegisterWindowMessage('{4F775E08-98FB-41b0-9852-10C7C13DD81C}'); end; In der überschriebenen Fensterprocedure wir der Thread zum wegspeichern der Daten angestoßen, sobald Daten von den Get_Data-Thread verfügbar sind:
Delphi-Quellcode:
procedure TMainForm.WndProc(var Message: TMessage);
begin if (Message.Msg = WM_GET_DATA_COMPLETE) then begin // Wenn Head(-pointer) <> Tail(-pointer) -> alles OK if FRingBuffer.Head <> FRingBuffer.Tail then begin // SaveDataThread aufwecken um gerade bekommenen Daten auf HD zu speichern FSaveDataThread.Resume; end else begin // Fehler. Head hat den Tail eingeholt :-( MessageBox(0, pWideChar('Overrun!'), '', MB_ICONSTOP or MB_OK); end; end; inherited; end; Im GetData-Thread wird die Funktion Get_Data ausgeführt, der "Head-Pointer" wird inkrementiert und die Fensterprocedure wird benachrichtigt, dass nun Data vorliegen. Unmittelbar danach wird die Get_Data-Funktion erneut aufgerufen:
Delphi-Quellcode:
{ TGetDataThread }
procedure TGetDataThread.Execute; begin while not terminated do begin ret := Get_Data(@FRingBuffer.pDataArray[FRingBuffer.Head][1]); FRingBuffer.IncHead; SendMessage(MainForm.Handle, MainForm.WM_GET_DATA_COMPLETE, 0, 0); end; end; Im SaveData-Thread werden die Daten solange weggespeichert, bis der "Tail-Pointer" den "Head-Pointer" erreicht hat. Ist dies der Fall liegen (noch) keine weiteren Daten vor:
Delphi-Quellcode:
{ TSaveDataThread }
procedure TSaveDataThread.Execute; begin repeat repeat FFileStream.Write(FRingBuffer.pDataArray[FRingBuffer.Tail][1], NUMBER_OF_DATA_BYTES); FRingBuffer.IncTail; until (FRingBuffer.Tail = FRingBuffer.Head); suspend; until (Terminated); FreeAndNil(FFileStream); end; Hier nun der Ringbuffer mit den Critical Sections:
Delphi-Quellcode:
Vielen Dank im Vorrauch für jegliche Tipps, Hinweise und Verbesserungsvorschläge. :dp:
type
TBuffer = array[0..10, 1..65536] of word; TpBuffer = ^TBuffer; type TRingBuffer = Class (TObject) private FDataArray : TBuffer; FpDataArray : TpBuffer; FHead : Integer; FTail : Integer; FCSHead : TCriticalSection; FCSTail : TCriticalSection; function GetHead: Integer; function GetTail: Integer; public Constructor Create; Destructor Destroy; override; procedure IncHead; procedure IncTail; property Head: Integer read GetHead; property Tail: Integer read GetTail; property pDataArray: TpRolBufDataOfAllCCDs read FpDataArray; end; implementation uses SysUtils; { TRingBuffer } constructor TRingBuffer.Create; begin FpDataArray := @FDataArray; FHead := 0; FTail := 0; FCSHead := TCriticalSection.Create; FCSTail := TCriticalSection.Create; end; //------------------------------------------------------------------------------ destructor TRingBuffer.Destroy; begin FreeAndNil(FCSHead); FreeAndNil(FCSTail) end; //------------------------------------------------------------------------------ function TRingBuffer.GetHead: Integer; begin FCSHead.Enter; result := FHead; FCSHead.Leave; end; //------------------------------------------------------------------------------ function TRingBuffer.GetTail: Integer; begin FCSTail.Enter; result := FTail; FCSTail.Leave; end; //------------------------------------------------------------------------------ procedure TRingBuffer.IncHead; begin FCSHead.Enter; inc(FHead); if FHead > 10 then FHead := 0; FCSHead.Leave; end; //------------------------------------------------------------------------------ procedure TRingBuffer.IncTail; begin FCSTail.Enter; inc(FTail); if FTail > 10 then FTail := 0; FCSTail.Leave; end; |
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Also erst entfernst du im Delphi-Treff ein paar Minuten nach deinem Post den Text und fragst dann direkt hier nach. Willst du hier auch den Beitrag gleich wieder leeren?
Jetzt habe ich wirklich gar keine Lust mehr etwas dazu zu schreiben nachdem ich den Post drüben direkt wieder verwerfen konnte. :evil: |
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Zitat:
Zitat:
|
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Zitat:
Zitat:
Was ich eben meinte war, dass ich gerade einiges zu deinem Quelltext geschrieben hatte und als ich es abschicken wollte war dein Beitrag schon leer... Zum Quelltext habe ich keine Lust das noch einmal zu schreiben. Aber dann wollte ich noch auf Pipes hinweisen. Diese eignen sich sehr gut zur Datenweitergabe in eine Richtung, in deinem Fall vom Thread, der die Berechnungen durchführt zu dem, der die Speicherung macht. Ich weiß nicht wie gut das bei dir passt, könnte aber evtl. einen fließenden Datenstrom ermöglichen statt wie bisher alle x Millisekunden. Denn bei einer Pipe ist das Schöne, dass der Thread jeweils einfach darauf warten kann, dass neue Daten ankommen und dann diese direkt verarbeiten kann. |
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
|
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Vielen Dank für Eure Antworten :!:
Über Pipes bin ich gerade am lesen... Interlockedxxxx- Funktionen werde ich mir danach anschauen... Zitat:
Könntest Du das evtl. etwas genauer ausführen? Das Prinzip ist mir nicht ganz klar geworden :oops: |
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Zitat:
|
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Zitat:
Delphi-Quellcode:
Aber bin ich damit wirklich auf der sicheren Seite?
procedure TRingBuffer.IncHead;
begin InterlockedIncrement(FHead); if FHead > 10 then FHead := 0; end; Folgendes Szenario: InterlockedIncrement wird ausgeführt. In diesem Moment kann die Variable nicht von anderen Threads gelesen werden. Angenommen der Wert von FHead sei vor dem inkrementieren 10 und danach folglich 11. Diesen 'Überlauf' fange ich zwar mit der folgenden if-Abfrage ab, aber was passiert wenn ein anderer Thread den Wert ausliest bevor diese geschehen ist? Dann hat er den Wert 11, sollte aber 0 haben :gruebel: |
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Du solltest dir einmal anschauen was es da noch alles für Interlocked-Funktionen gibt. ;-)
Unter anderem InterlockedExchange, InterlockedAnd, InterlockedXor, ... ;-) Siehe Dokumentation: ![]() // EDIT: Allerdings könnte ein anderer Thread trotzdem den Wert zwischen den beiden Operationen auslesen. |
Re: Daten parallel mithilfe eines Ringbuffers wegspeichern -
Zitat:
Delphi-Quellcode:
if FHead<10 then
InterlockedIncrement(FHead) else InterlockedExchangeAdd(FHead,-9); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:51 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-2025 by Thomas Breitkreuz