Einzelnen Beitrag anzeigen

Zwoetzen

Registriert seit: 19. Sep 2007
Ort: Ilmenau
93 Beiträge
 
Delphi 2009 Professional
 
#1

[Threads] Textureloader - Einige Fragen

  Alt 20. Nov 2008, 17:48
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;
  Mit Zitat antworten Zitat