Einzelnen Beitrag anzeigen

Viktorii

Registriert seit: 19. Jul 2007
358 Beiträge
 
#1

Daten parallel mithilfe eines Ringbuffers wegspeichern - Cri

  Alt 8. Jun 2009, 08:00
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.
  Mit Zitat antworten Zitat