![]() |
Problem beim Zugriff auf TObjectList aus Threads
Da ich in meinem aktuellen Projekt etliche Probleme mit dem Multithreading habe, habe ich nun ein kleines Testprogramm geschrieben, um dem dem Übel auf den Grund zu gehen. Dabei habe ich eine seltsame Entdeckung gemacht.
Zunächst habe ich einen Sample-Thread geschrieben, der wie folgt aussieht:
Delphi-Quellcode:
Den Thread starte ich nun von einem "Manager" aus, der nichts anderes macht, als bei jedem Aufruf TSampleThread einen freien Slot zuzuweisen und diesen darüber auszuführen. Sind gerade alle 20 Slots besetzt, so wird der Thread nicht ausgeführt.
TSampleThread= class(TThread)
TestList: TObjectList; protected procedure Execute; override; public constructor Create; virtual; end; constructor TSampleThread.Create; begin inherited Create(False); FreeOnTerminate := False; TestList := TObjectList.Create; end; procedure TSampleThread.Execute; var j, Rnd: integer; begin j := 1; repeat Rnd := Random(10); inc(j); until j = 10; Terminate; end;
Delphi-Quellcode:
Das ganze klappt bis dahin wunderbar. Auch wenn ich TSampleThread mittels eines TTimer-Objekts durch die Funktion TThreadManager.Add bis zu 1000 Mal pro Sekunde aufrufe, läuft alles wie geschmiert.TThreadManager = class public Thread: array[1..20] of TSampleThread; procedure Add; end; procedure TThreadManager.Add; var i: integer; FreeSlot: boolean; begin i := 0; FreeSlot := False; repeat inc(i); if Assigned(Thread[i]) = True then begin if Thread[i].Terminated = True then FreeSlot := True; end else FreeSlot := True; until (FreeSlot = True) or (i = 20); if FreeSlot = True then Thread[i] := TSampleThread.Create; end; Das Problem tritt erst auf, wenn ich im Thread auf ein threadinternes TObjectList-Object zugreife, z.B. auf dessen Eigenschaft Count:
Delphi-Quellcode:
Die einzige Zeile, die ich verändert habe, ist die, wo der Random-Wert bestimmt wird. Ansonsten bleibt alles gleich.
procedure TSampleThread.Execute;
var j, Rnd: integer; begin j := 1; repeat Rnd := Random(TestList.Count + 10); inc(j); until j = 10; Terminate; end; Wenn ich nun aber meinen Thread so wie oben beschrieben sehr oft pro Sekunde ausführe, kommt es früher oder später zum Absturz ("Project1 hat ein Problem festgestellt und muss beendet werden"). Auf den ersten Blick sieht also alles nach einem Synchronizations-Problem aus. Aber daran kann es doch in diesem Fall nicht liegen. Denn jeder Thread hat sein eigenes TestList-Objekt und greift völlig unabhängig von den anderen exklusiv auf dieses zu!? Die Threads kommen sich auf keinen Fall beim Zugriff auf TestList in die Quere. Und trotzdem schmiert mein Proggi ab. Wäre froh um jeden Ratschlag, denn ich finde das ganze einfach zu verwirrend. Nachtrag: So klappt es...
Delphi-Quellcode:
constructor TSampleThread.Create;
begin inherited Create(False); FreeOnTerminate := False; TestList := TObjectList.Create; test := TestList.Count; // = 0 end; procedure TSampleThread.Execute; var j, Rnd: integer; begin j := 1; repeat Rnd := Random(test); inc(j); until j = 10; Terminate; end; |
Re: Problem beim Zugriff auf TObjectList aus Threads
Ein paar Gedanken von mir dazu:
- du gibst nirgendwo die TObjectList Instanz wieder frei aus dem Thread - du gibst nirgendwo beendete Threads wieder frei - vergleiche niemals auf true - warum ist der Constructor des Threads virtuell? - du benutzt die Terminate Methode falsch, da diese nur das Terminated Flag setzt und somit eine Kennzeichnung für den Nutzer von TThread sein soll, die Execute-Methode zu verlassen. Und dein Hauptfehler: - du lässt den Thread sofort loslaufen, und das bevor der Constructor abgearbeitet ist und somit bevor die ObjectList Instanz gebildet wurde. Zu deinem Edit: alle Member einer Klasse sowie globale Objekte werden auf ordinal 0 initialisiert. /EDIT: nochmal alle Hinweise umgesetzt:
Delphi-Quellcode:
type
TSampleThread= class(TThread) TestList: TObjectList; protected procedure Execute; override; public constructor Create; destructor Destroy; override; end; constructor TSampleThread.Create; begin inherited Create(true); FreeOnTerminate := False; TestList := TObjectList.Create; Resume; end; destructor TSampleThread.Destroy; begin TestList.Free; inherited; end; procedure TSampleThread.Execute; var j, Rnd: integer; begin j := 1; while not Terminated and ( j < 10 ) do begin Rnd := Random(TestList.Count + 10); inc(j); end; end;
Delphi-Quellcode:
TThreadManager = class
public Thread: array[1..20] of TSampleThread; procedure Add; end; procedure TThreadManager.Add; var i: integer; FreeSlot: boolean; begin i := 0; FreeSlot := False; repeat inc(i); FreeSlot := ( assigned(Thread[i]) and Thread[i].Terminated ) or not assigned(Thread[i]); until FreeSlot or (i = 20); if FreeSlot then begin Thread[i].Free; Thread[i] := TSampleThread.Create; end; end; |
Re: Problem beim Zugriff auf TObjectList aus Threads
Edit X: (also nochmal)
Zitat:
Demnach ist es egal wann welche Variablen initialisiert werden bei TThread. Und oben genanntes ist kein Fehler |
Re: Problem beim Zugriff auf TObjectList aus Threads
Kann es sein das du überhaupt nicht syncronisiert auf den Thread zugreist?
Deswegen kracht es bei dem .Count weil du bei 1000 in der sekunde sehr wahrscheinlich einmal gleichzeitig lesen und schreiben willst |
Re: Problem beim Zugriff auf TObjectList aus Threads
Zitat:
Was Sirius geschrieben hat, kann ich zwar aufgrund meiner bescheidenen Kenntnisse nicht beurteilen. Aber irgendwie wäre es wirklich seltsam, wenn es so wäre, wie Du es geschrieben hast. Wenn ein Create(False) tatsächlich so unsicher ist, wozu kann man es denn überhaupt gebrauchen? |
Re: Problem beim Zugriff auf TObjectList aus Threads
Gibt es nicht eine TThreadList Klasse??
|
Re: Problem beim Zugriff auf TObjectList aus Threads
Hallo Dezipaitor,
ja gibt es. Bis bald Chemiker |
Re: Problem beim Zugriff auf TObjectList aus Threads
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:01 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz