Eine vollständige Lösung sähe vielleicht folgendermaßen aus. Wie gesagt: das ist jetzt etwas schlechter von der Performance her und auch etwas größer. Deswegen würde ich bei deinem Problem auch etwas kleineres wählen. Beim Schreiben dieses Codes fielen mir aber noch zwei Probleme in deinem Code auf.
- Threadsteuerung mit Suspend und Resume zu machen ist nicht so gut und wird von M$ nicht empfohlen. Besser sind Events.
- Was machst du, wenn dein GetDataThread den SaveDatathread einholt und beide auf das gleiche Array schreiben? Bzw.: Wie verhinderst du dies?
Ich habe mal ein Beispiel versucht.
Also die Idee ist eine Klasse, welche die CS und den Zugriff auf den Inhalt verwaltet:
Delphi-Quellcode:
type
//Erstmal packen wir das Array in eine Klasse, wenn auch noch sehr rudimentär
TDataArray=class(TPersistent)
public
//ist mal nicht mit einem property gekapselt, weil ich eh nur den Pointer darauf benötige
//evtl. wird das ja hier noch anders verwendet
Values:array[1..65536] of word;
protected
//zum Kopieren der Klasse
procedure AssignTo(Dest: TPersistent); override;
end;
//der Zweite Teil des Array wird eine List
//hier eine TObjectList nur mit angpeassten Methoden, damit man nicht
//ständig außerhalb mit dem As-Operator handeln muss
//zudem wird noch auf Notify reagiert
TDataList=class(TObjectList)
private
FonAdd:TNotifyevent;
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
function GetItem(Index: Integer): TDataArray;
procedure SetItem(Index: Integer; ADataArray: TDataArray);
public
property Items[Index: Integer]: TDataArray read GetItem write SetItem; default;
property OnAdd:TNotifyEvent read FonAdd write FonAdd;
end;
//Kapselung der Liste mit einer CS
TThreadDataList=class
Constructor Create; reintroduce;
Destructor Destroy; override;
private
FList:TDataList;
FLock:TCriticalSection;
public
procedure Add(Item: TDataArray);
function LockList: TDataList;
procedure UnlockList;
end;
Die "eigentliche" Klasse ist TThreadDataList. Darin enthalten ist eine Liste(TDataList). Und diese Liste beinhaltet das Array (TDataArray)
TDataArray hat nur das Array für einen Lese-Vorgang und noch die AssignTo Methode um die Klasse zu kopieren (geerbt von TPersistent).
TDataList verdeckt eigentlich nur die Methoden von TObjectList um aus den Parametern TObject TDataArray zu machen. Ist dann einfacher in der Anwendung. Außerdem wird Notify überschrieben, so dass darin reagiert werden kann, wann die Liste >10 Einträge hat, damit der älteste gelöscht wird. Außerdem kann ein Ereigniss ausgelöst werden (FonAdd)
TThreadDataList hat nur drei Methoden. Add um einen Eintrag der obigen Liste hinzuzufügen (mit CS gekapselt) und das Paar LockLsit und Unlocklist um die Liste nur bei gleichzeitiger Synchronisation zu bekommen (siehe dazu TThreadList)
Der Code hierzu ist:
Delphi-Quellcode:
function TDataList.GetItem(Index: Integer): TDataArray;
begin
result:=inherited GetItem(Index) as TDataArray;
end;
procedure TDataList.Notify(Ptr: Pointer; Action: TListNotification);
begin
inherited;
if Action=lnAdded then
begin
while Count>10 Do Delete(0);
if assigned(FonAdd) then FonAdd(self); //siehe TSaveDataThread.ListAdd
end;
end;
procedure TDataList.SetItem(Index: Integer; ADataArray: TDataArray);
begin
inherited SetItem(Index,ADataArray);
end;
{ TThreadDataList }
procedure TThreadDataList.Add(Item: TDataArray);
begin
LockList;
try
FList.Add(Item);
finally
UnlockList;
end;
end;
constructor TThreadDataList.Create;
begin
FLock:=TCriticalSection.Create;
FList:=TDataList.Create(True);
end;
destructor TThreadDataList.Destroy;
var List:TList;
begin
LockList;
try
FList.Free;
inherited;
finally
FLock.Free;
end;
end;
function TThreadDataList.LockList: TDataList;
begin
FLock.Enter;
result:=FList;
end;
procedure TThreadDataList.UnlockList;
begin
FLock.Leave;
end;
{ TDataArray }
procedure TDataArray.AssignTo(Dest: TPersistent);
begin
inherited;
(Dest as TDataArray).FValues:=FValues;
end;
Die Threads habe ich mal kurz so implementiert:
Delphi-Quellcode:
type
//BeispielThreads:
TDataThread=class(TThread)
Constructor Create(CreateSuspended:Boolean; ThreadDataList:TThreadDataList); reintroduce;
protected
FThreadDataList:TThreadDataList;
end;
TGetDataThread=class(TDataThread)
protected
procedure Execute; override;
end;
TSaveDataThread=class(TDataThread)
protected
procedure Execute; override;
procedure ListAdd(Sender:TObject);
private
FEvent:TEvent;
public
procedure Terminate; reintroduce;
end;
//...
{ TDataThread }
constructor TDataThread.Create(CreateSuspended: Boolean;
ThreadDataList: TThreadDataList);
begin
inherited Create(CreateSuspended);
FThreadDataList:=ThreadDataList;
end;
{ TGetDataThread }
procedure TGetDataThread.Execute;
var DataArray:TDataArray;
List:TDataList;
begin
while not terminated do
begin
DataArray:=TDataArray.Create;
Get_Data(@DataArray.Values);
List:=FThreadDataList.LockList;
try
List.Add(DataArray);
finally
FThreadDataList.UnlockList;
end;
end;
end;
{ TSaveDataThread }
procedure TSaveDataThread.Execute;
var List:TDataList;
DataArray:TDataArray;
isEntry:Boolean;
begin
FEvent:=TEvent.Create(nil,false,true,'');
List:=FThreadDataList.LockList;
try
List.OnAdd:=ListAdd;
finally
FThreadDataList.UnlockList;
end;
while not terminated do
begin
FEvent.WaitFor(infinite);
repeat
List:=FThreadDataList.LockList;
try
isEntry:=List.Count>0;
if isEntry then
begin
DataArray.Assign(List.Items[0]);
List.Delete(0);
end;
finally
FThreadDataList.UnlockList;
end;
if isEntry then
begin
FFileStream.Write(DataArray.Values[1], NUMBER_OF_DATA_BYTES);
end;
until not isEntry;
end;
end;
procedure TSaveDataThread.ListAdd(Sender: TObject);
begin
FEvent.SetEvent;
end;
procedure TSaveDataThread.Terminate;
begin
inherited;
FEvent.SetEvent;
end;
die MEssage brauche ich gar nicht mehr, ich benutze einfach das Event. In Waitfor wartet der Thread, bis die Liste über ListAdd das Event setzt und der Durchlauf einmal beginnt, bis er wieder bei Waitfor ist.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.