Wenn du die Live-Werte extra vorhälst, dann wird FLast nicht benötigt und eine stinknormale einfach verkettete Liste reicht vollkommen aus.
ach nee, für's Anhängen neuer Daten wird FLast ja dennoch benötigt.
Delphi-Quellcode:
uses
Windows, Classes, SysUtils, SyncObjs;
type
POneData = ^TOneData;
TOneData =
record
Text:
string;
Value1: Double;
Value2: Double;
Value3: Double;
Value4: Double;
Next: POneData;
end;
TDataClass =
class
private
FEvent: TEvent;
FLock: TCriticalSection;
FFirst, FLast: POneData;
FLive: TOneData;
public
constructor Create;
destructor Destroy;
override;
procedure Add(
const Name:
string;
const V1, V2, V3, V4: Double);
function ExtractAll: POneData;
function GetLive: TOneData;
function WaitAndCheckForNewData(TimeOut: Cardinal): Boolean;
end;
TWorker =
class(TThread)
private
FDataClass: TDataClass;
protected
procedure Execute;
override;
public
constructor Create(DataClass: TDataClass);
end;
constructor TDataClass.Create;
begin
inherited Create;
FEvent := TEvent.Create(
nil, True, False, '
');
FLock := TCriticalSection.Create;
end;
destructor TDataClass.Destroy;
var
tmp: POneData;
begin
while FFirst <>
nil do
begin
tmp := FFirst;
FFirst := FFirst.Next;
Dispose(tmp);
end;
FEvent.Free;
FLock.Free;
inherited Destroy;
end;
procedure TDataClass.Add(
const Name:
string;
const V1, V2, V3, V4: Double);
var
NewData: POneData;
begin
New(NewData);
NewData.Text :=
Name;
NewData.Value1 := V1;
NewData.Value2 := V2;
NewData.Value3 := V3;
NewData.Value4 := V4;
NewData.Next :=
nil;
try
FLock.Acquire;
try
FLive := NewData^;
if Assigned(FLast)
then
begin
FLast.Next := NewData;
FLast := NewData;
end else
begin
FFirst := NewData;
FLast := FFirst;
end;
FEvent.SetEvent;
finally
FLock.Release;
end;
except
Dispose(NewData);
end;
end;
function TDataClass.ExtractAll: POneData;
begin
FLock.Acquire;
try
FEvent.ResetEvent;
Result := FFirst;
FFirst :=
nil;
FLast :=
nil;
finally
FLock.Release;
end;
end;
function TDataClass.GetLive: TOneData;
begin
FLock.Acquire;
try
Result := FLive;
finally
FLock.Release;
end;
end;
function TDataClass.WaitAndCheckForNewData(TimeOut: Cardinal): Boolean;
begin
Result := FEvent.WaitFor(TimeOut) = wrSignaled;
end;
procedure TWorker.Execute;
var
PData, PData2: POneData;
begin
repeat
if FDataClass.WaitAndCheckForNewData(100)
then
begin
PData := FDataClass.ExtractAll;
try
while Assigned(PData)
do
begin
PData2 := PData;
PData := PData.Next;
try
Verarbeite(PData2^);
finally
Dispose(PData2);
end;
end;
except
// falls Exception, restlichen Speicher aufräumen und Werte verwerfen
// aber es wäre auch möglich die Daten wieder in die Liste einzufügen
// oder man "ignoriert" Exceptions und setzt einfach die obere Schleife fort
while Assigned(PData)
do
begin
PData2 := PData;
PData := PData.Next;
Dispose(PData2);
end;
Raise;
end;
end;
until Terminated;
end;
constructor TWorker.Create(DataClass: TDataClass);
begin
inherited Create(True);
FDataClass := DataClass;
Resume;
end;
Da hier gleich die ganze Liste aus der Datenhaltung rausgeholt wird, ist diese bereit sofort neue Daten aufzunehmen und wird weniger bei Auslesen blockiert.
Bei Verarbeite werden die Daten dann einfach in beide Dateien (Rohdaten und umgerechneten
CSV-Daten geschrieben).
Die aktuellsten Live-Werte bekommt man über DataClass.GetLive geliefert.