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.
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:
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;
Vielen Dank im Vorrauch für jegliche Tipps, Hinweise und Verbesserungsvorschläge.