Hi alle zusammen,
ich bin gerae dabei, eine
OpenGL-Anwendung zu schreiben. In diesem Programm müssen je nach aktueller Situation Texturen nachgeladen werden (eben die, die als nächstes gebraucht werden). Das ganze möchte ich (bzw. habe ich schon) in einen Thread auslagern, um das Programm selbst nicht unnötig zu stören.
Unten habt ihr den entsprechenden Quellcode von meiner TTextureLoader-Klasse.
Meine Fragen diesbezüglich sind nun folgende:
- Ich habe gehört, dass man für gemeinsam genutzten Speicher CriticalSections einsetzen muss. Ich denke, dass dies bei meiner FQueue der Fall ist. Doch wie genau muss das hier aussehen? Einfach um die ganzen FQueue-Methoden-Afurufen dieses Enter/Leave-Dingens drumsetzen?
- Um nicht immer einen neuen Thread anlegen zu müssen, versuche ich, den einen, der bei Programmstart angelegt wird, am Leben zu behalten. Ist die Vorgehensweise, den Thread bei "arbeitslosigkeit" schlafen zu legen und beim Hinzufügen eines neuen Jobs wieder aufzuwekcen elegant oder sollte man sowas vermeiden? ^^ (Will keinen Müll schreiben, deshalb diese Frage )
- Zum Verständnis: Was genau macht eigentlich Synchronize und wann muss ich es verwenden? Ich habe gelesen, dass das irgendwas mit dem Context des Threads zu tun hat, weswegen das GenTexture() auch mit Synchronize aufgerufen werden muss. Aber inwiefern muss ich andere Methoden ebenfalls damit aufrufen (wie zB meine FinishProc der Jobs, SyncSucceeded und SyncFailed)?
- Wie ist das, wenn ich den Thread am Ende wieder freigeben will: Mein logischer Verstand sagt mir, dass ein Terminate() allein nicht reichen kann, da der Thread ja eventuell noch schläft. Muss ich also vorher ein Resume() aufrufen, und danach ein Terminate(), oder andersrum? (Bei ersterem könnte es ja passieren, dass der Thread sofort wieder wegnickt, weil die FQueue ja noch leer ist. Bei zweiterem bin ich aber nicht sicher, ob das richtig ist... )
Würde mich freuen, wenn ihr ein paar erklärende Worte niederschreiben könntet, damit ich zum einen keine Fehler einbaue, und zum anderen den Sachverhalt Threads wieder ein wenig mehr verstehe
MfG Zwoetzen
____________________________________
Delphi-Quellcode:
type
TFinishProc =
procedure(Succeeded: Boolean)
of Object;
TJob =
class
Path:
String;
Texture: TglBitmap2D;
OnFinish: TFinishProc;
end;
TTextureLoader =
class(TThread)
private
FQueue: TObjectQueue;
// Speichert die aktuellen Aufträge
FCurJob: TJob;
// Aktueller Job (für die Synchronize-Prozeduren)
procedure SyncGenTex;
procedure SyncFailed;
procedure SyncSucceeded;
public
constructor Create;
destructor Destroy;
override;
procedure AddTexture(Path:
String; Texture: TglBitmap2D;
OnFinish: TFinishProc =
nil);
procedure Execute;
override;
end;
[...]
procedure TTextureLoader.AddTexture(Path:
string; Texture: TglBitmap2D;
OnFinish: TGFinishProc =
nil);
// Das Texture-Objekt wird immer schon vor Aufruf dieser Funktion angelegt.
// OnFinish bietet die Möglichkeit, "Bescheid" zu geben, wenn die Textur fertig ist oder nicht geladen werden kann
var
Job: TJob;
begin
if FileExists(Path)
and Assigned(Texture)
then begin
Job := TJob.Create;
Job.Path := Path;
Job.Texture := Texture;
Job.OnFinish := OnFinish;
// Brauch ich für diese Anweisung jetzt eine CriticalSection? (Ich denk mal schon)
FQueue.Push(Job);
// Falls Thread angehalten wurde, wieder aufnehmen
if Suspended
then
Resume;
end;
end;
procedure TTextureLoader.Execute;
begin
while not Terminated
do begin
// Schlafen legen, wenn keine Jobs mehr vorhanden sind, um den Thread zu erhalten
if (FQueue.Count = 0)
then
Suspend
else begin
// Hier auch eine CriticalSection?
FCurJob := MTGJob(fQueue.Pop);
// Eventuell noch ein try..finally rumbasteln, um FCurJob sicher freizugeben und einen Fehler in SyncFailed abzufangen
try
FCurJob.Texture.LoadFromFile(FCurJob.Path);
Synchronize(SyncGenTex);
Synchronize(SyncSucceeded);
FCurJob.Free;
except
on E:
Exception do
Synchronize(SyncFailed);
end;
// try..except
end;
// if..then..else
end;
// while
end;
procedure TTextureLoader.SyncGenTex;
begin
FCurJob.Texture.GenTexture;
end;
// Inwiefern sind die folgenden zwei Synchronize-Prozeduren wirklich erforderlich?
procedure TTextureLoader.SyncFailed;
begin
if Assigned(FCurJob.OnFinish)
then
FCurJob.OnFinish(False);
end;
procedure TTextureLoader.SyncSucceeded;
begin
if Assigned(FCurJob.OnFinish)
then
FCurJob.OnFinish(True);
end;