Ich grabe den Thread noch einmal aus.
In der Regel verfolge ich den Ansatz, dass sich jede Objekt-Instanz, in der Daten manipuliert werden können, selbst absichert.
Bisher hat das immer recht gut funktioniert.
In meinem aktuellen Projekt such ich mir aber schon seit Tagen den Wolf wegen diverser spontaner Deadlocks.
Ich zweifle daran, ob ich das Locking richtig verstanden habe
Liegt das generell an meiner "Art" die Criticalsections zu bauen?
Wenn ja, gebt mir doch bitte kurz Bescheid, dann kann ich mir das durchsuchen von 100ten units ersparen und verwende globale Variablen für die CriticalSections.
Hier mal 2 vereinfachte Beispiele:
Bei der folgenden "Jobliste für WorkerThreads" sollte doch ein Thread Jobs hinzufügen und mehrere Workerthreads Jobs abholen können oder?
Und... kann ein Timer im MainForm sicher
label1.caption := inttostr(MyJoblist.count));
aufrufen?
Delphi-Quellcode:
type
TJoblist = class(TObjectList) // in Wahrheit typensicherere Objectlist
private
FCS: TCriticalSection;
public
constructor Create; reintroduce;
destructor Destroy; override;
function GetNextJob(out aJob: TJob): boolean;
procedure AddJob(aJob: TJob);
end;
...
...
implementation
{ TJoblist }
procedure TJoblist.AddJob(aJob: TJob);
begin
FCS.Enter;
try
Add(aJob);
finally
FCS.Leave;
end;
end;
constructor TJoblist.Create();
begin
inherited Create(True); // OwnsObjects
FCS := TCriticalSection.Create;
end;
destructor TJoblist.Destroy;
begin
FCS.Enter;
try
Clear;
finally
FCS.Leave;
end;
FCS.Free;
inherited;
end;
function TJoblist.GetNextJob(out aJob: TJob): boolean;
begin
FCS.Enter;
try
if Count > 0 then
begin
aJob := TJob(Extract(Items[0]));
result := assigned(aJob);
end
else
begin
aJob := nil;
result := false;
end;
Finally
FCS.Leave;
end;
// ReleaseSemaphore usw. ...
end;
Meine WorkerThreads haben auch eine "interne" CriticalSection.
Über diese sichere ich z.B. die Setter zum aktualisieren von Thread-Properties ab.
Ist die "exemplarische" <Status> Property Thread-Safe?
Also.. könnte mein "Timer auf dem Mainform" beispielsweise sicher durch den Workerthread-Pool laufen und alle Workerthread-Staties in eine Listbox schreiben?
Delphi-Quellcode:
Listbox1.clear;
for i:= 0 to Threadpool.Count -1 do
begin
Listbox1.items.add('Thread-'+inttostr(i)+' Status : '+ StatusToString( ThreadPool[i].Status) );
end;
Hier die vereinfachte Thread-Klasse:
Delphi-Quellcode:
{$M+}
TMyThread = class(TThread)
private
{ private-Deklarationen }
FCS: TCriticalSection;
fStatus: TThreadStatus;
fJoblist: TJoblist;
procedure SetStatus(const Value: TThreadStatus);
procedure SetJoblist(const Value: TJoblist);
protected
{ protected-Deklarationen }
procedure Execute; override;
public
{ public-Deklarationen }
constructor Create(); reintroduce;
destructor Destroy; override;
published
{ published-Deklarationen }
property Status: TThreadStatus read fStatus write SetStatus;
property JobList: TJoblist read fJoblist write SetJoblist;
end;
...
implementation
...
{ TMyThread }
constructor TMyThread.Create();
begin
inherited Create(true);
FCS := TCriticalSection.Create;
FreeOnTerminate := False; // wird von extern freigegeben;
end;
destructor TMyThread.Destroy;
begin
FCS.Free;
inherited;
end;
procedure TMyThread.Execute;
var ThreadJob: TJob;
begin
inherited;
Status := stConfig;
// ...
while not Terminated do
begin
Status := stIdle;
// WaitForMultipleObjects( .....)
Status := stWorking;
Assert(assigned(Joblist),'keine Joblist zugewiesen');
if JobList.GetNextJob( ThreadJob ) then
begin
// .. mach was mit dem Job
// ThreadJob.Free;
end;
end;
Status := stFinalize;
// abschließende Schritte ...
Status := stTerminated;
end;
procedure TMyThread.SetStatus(const Value: TThreadStatus);
begin
FCS.Enter;
try
fStatus := Value;
finally
FCS.Leave;
end;
end;
procedure TMyThread.SetJoblist(const Value: TJoblist);
begin
FCS.Enter;
try
fJoblist := Value;
finally
FCS.Leave;
end;
end;
Grüße,
Jens