Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Anwendungsinitialisierung in Thread auslagern (https://www.delphipraxis.net/162316-anwendungsinitialisierung-thread-auslagern.html)

Codehunter 17. Aug 2011 09:39

Delphi-Version: 5

Anwendungsinitialisierung in Thread auslagern
 
Hallo!

In der .dpr gibt es ja bei größeren Projekten eine ganze Reihe von Application.CreateForm-Zeilen. Die ganzen Formulare die da erzeugt werden verzögern den Programmstart zum Teil erheblich.

Da nicht alle Formulare sofort gebraucht werden, habe ich das Erzeugen aus der .dpr in die Haupt-Unit verlegt und führe es dort erst bei der Nachricht WM_AFTERSHOW (Formular fertig auf dem Bildschirm) aus. Dadurch startet das Projekt zwar erstmal blitzschnell, werkelt dann aber noch eine Weile nach.

Entweder lasse ich es werkeln oder ich füge zwischen jedes CreateForm noch ein ProzessMessages, dann ist das Programm soweit arbeitsfähig. Das Problem dabei: Wird während der noch laufenden CreateForm-Schlange ein Formular geöffnet, dann werden keine weiteren CreateForm-Anweisungen ausgeführt. Daher mein Gedanke, das ganze Initialisierungsgeraffel in einen separaten Thread auszulagern.

Was meint ihr dazu?

Grüße
Cody

DeddyH 17. Aug 2011 09:44

AW: Anwendungsinitialisierung in Thread auslagern
 
Müssen denn alle Formulare sofort geladen werden oder ginge das nicht auch bei Bedarf zur Laufzeit?

chaosben 17. Aug 2011 09:45

AW: Anwendungsinitialisierung in Thread auslagern
 
Im eigenen Thread wirds nicht so einfach, wegen der Synchronisierung.
Ich würde es an deiner Stelle mit dem Application.OnIdle-Event probieren ... da bleibt man im Application-Thread, weiß aber, das die App gerade nix zu tun hat.

Sowas ähnliches hab ich letztens umgesetzt und es funktioniert sehr gut.

Andreas L. 17. Aug 2011 09:51

AW: Anwendungsinitialisierung in Thread auslagern
 
Ich würde die Forms (wie DeddyH sagt) wenn möglich erst zur Laufzeit erzeugen:

Delphi-Quellcode:
type
  TFormIrgendwas = class(TForm)
    //...
  public
    class function Execute: Boolean;
  end;

...
...

class function TFormIrgendwas.Execute: Boolean;
begin
  with TFormIrgendwas.Create(nil) do
  begin
    Result := ShowModal = mrOK;
    Free;
  end;
end;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  if TFormirgendwas.Execute then
    ShowMessage('Fenster mit "OK" geschlossen')
  else
    ShowMessage('Fenster mit "Abbrechen" geschlossen');
end;

DeddyH 17. Aug 2011 09:54

AW: Anwendungsinitialisierung in Thread auslagern
 
class function wird Delphi 7 wohl nicht nehmen wollen IIRC, aber das geht ja auch ganz konservativ.
Delphi-Quellcode:
if not Assigned(FormBla) then
  FormBla := TFormBla.Create(Application);
FormBla.Show;

Bernhard Geyer 17. Aug 2011 09:55

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von chaosben (Beitrag 1117452)
Im eigenen Thread wirds nicht so einfach, wegen der Synchronisierung.

Sag wir lieber was die Formulareerstellung betrifft: Es ist unmöglich.

Aber wie schon gesagt: Keine Formular auf "Halte" anlegen. Auch deine verbrauchten GDI-Ressourcen werden es dir danken.

chaosben 17. Aug 2011 09:57

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von Andreas L. (Beitrag 1117454)
Ich würde die Forms (wie DeddyH sagt) wenn möglich erst zur Laufzeit erzeugen:

... bzw. in dem Moment, wenn man sie braucht.
Das ist imho die richtigste :-) Lösung.

chaosben 17. Aug 2011 10:31

AW: Anwendungsinitialisierung in Thread auslagern
 
Liste der Anhänge anzeigen (Anzahl: 2)
Die Idee (mit dem OnIdle) ist an sich ganz lustig.
Deswegen hab ich mal ne kleine Unit gebaut.
Die einfach im Projekt einbinden und alle
Delphi-Quellcode:
Application.CreateForm
durch
Delphi-Quellcode:
Application_CreateForm
ersetzen.

Mich würde mal interessieren, ob dir das weiterhilft.

Codehunter 17. Aug 2011 12:09

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von chaosben (Beitrag 1117475)
Deswegen hab ich mal ne kleine Unit gebaut.

Also die Idee mit dem OnIdle ist wirklich nicht schlecht. Mit der Konstruktion wird der ganze Stapel von anhängigen Formularen asynchron erzeugt und auch bei geöffneten Formularen fortgesetzt.

Bernhard hat schon vollkommen recht: Die sauberste Lösung wäre die dynamische Erzeugung der Formulare bei Bedarf. Leider kann man gewachsene Projekte nur recht schwer auf soetwas umstellen. Bei neuen Projekten werde ich das aber in Zukunft so machen.

Allgemein gesprochen: Das ganze Konstrukt der Delphi-IDE-Formular-Assistenten erzeugt nicht gerade effizienten Code. Per Default wird jedes einzelne Formular mit seinem gesamten GDI-Gerümpel beim Programmstart geladen. Das kann man bei meinem jetzigen "Problemkind" sehr schön sehen. Ohne die asynchrone Formularerzeugung dauert der Programmstart 8 Sekunden. Mit async. dagegen nur 0,4 bis das MainForm steht. In beiden Fällen kann man eine schöne ansteigende Kurve im GDI-Monitor beobachten. Würde man die ganzen Formulare nur erzeugen wenn man sie braucht und dann wieder freigeben, ich glaub ich könnte 80% Speicherbedarf einsparen.

Aber wie gesagt, das geht mit vertretbarem Aufwand nur bei neuen Projekten.

Grüße
Cody

FaTaLGuiLLoTiNe 17. Aug 2011 13:01

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von DeddyH (Beitrag 1117456)
class function wird Delphi 7 wohl nicht nehmen wollen IIRC

Klassenmethoden sind auch in D7 möglich, nur Klassenattribute gibt's da noch nicht.

DeddyH 17. Aug 2011 13:02

AW: Anwendungsinitialisierung in Thread auslagern
 
Ah, so war das, ich hatte es nicht mehr genau in Erinnerung. Danke :thumb:

Morphie 17. Aug 2011 13:25

AW: Anwendungsinitialisierung in Thread auslagern
 
btw.:
Ich glaube ich habe mal irgendwo aufgeschnappt, dass man dieses automatische Erstellen der Formulare deaktivieren kann, finde die Option aber nirgends.
Gibt es so eine Option doch nicht, oder bin ich nur blind? :(

DeddyH 17. Aug 2011 13:28

AW: Anwendungsinitialisierung in Thread auslagern
 
Liste der Anhänge anzeigen (Anzahl: 1)
In den Projektoptionen unter Formulare. Links sind die automatisch erstellten, rechts die verfügbaren.

QuickAndDirty 17. Aug 2011 14:23

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von DeddyH (Beitrag 1117456)
class function wird Delphi 7 wohl nicht nehmen wollen

doch! ;)

DeddyH 17. Aug 2011 14:30

AW: Anwendungsinitialisierung in Thread auslagern
 
Das haben wir doch bereits geklärt (#10 und #11).

Uwe Raabe 17. Aug 2011 15:26

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von Morphie (Beitrag 1117518)
btw.:
Ich glaube ich habe mal irgendwo aufgeschnappt, dass man dieses automatische Erstellen der Formulare deaktivieren kann, finde die Option aber nirgends.
Gibt es so eine Option doch nicht, oder bin ich nur blind? :(

Delphi 7: Tools - Umgebungsoptionen - Designer - Autom. Formulare und Datenmodule
Delphi XE: Tools - Optionen - Umgebungsoptionen - VCL-Designer - Autom. Formulare und Datenmodule

Die anderen starte ich jetzt nicht extra.

DeddyH 17. Aug 2011 15:29

AW: Anwendungsinitialisierung in Thread auslagern
 
:oops: Ach das war gemeint. Ich geh mich mal 'ne Runde schämen.

QuickAndDirty 17. Aug 2011 15:30

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von DeddyH (Beitrag 1117537)
Das haben wir doch bereits geklärt (#10 und #11).

Na und. Fürs klugscheißen gibt es kein "Das haben wir doch bereits geklärt" .

(habe es einfach erst hinterher gesehen ;) )

neo4a 17. Aug 2011 18:35

AW: Anwendungsinitialisierung in Thread auslagern
 
Zitat:

Zitat von Codehunter (Beitrag 1117501)
Bernhard hat schon vollkommen recht: Die sauberste Lösung wäre die dynamische Erzeugung der Formulare bei Bedarf. Leider kann man gewachsene Projekte nur recht schwer auf soetwas umstellen. Bei neuen Projekten werde ich das aber in Zukunft so machen.

Auch bei der Umstellung existierender Projekte kann ich Dir einen Ansatz zur Prüfung empfehlen, den ich derzeit mittels Emballo bereits umsetze: Hier nimmt Dir das Framework das Instanziieren ab und Du greifst auf Deine Formulare und Service-Komponenten nur noch über Funktionen zu. Das passiert über Interfaces, so dass es keine Unit-Referenzen erfordert.

In der .dpr steht dann bspw.
Delphi-Quellcode:
  //Services
  RegisterFactory(IdcXMLStore, TdcXMLStore).Singleton.Done;
  RegisterFactory(IdcSettings, TdcSettings).Singleton.Done;
  RegisterFactory(IdcLanguage, TdcLanguages).Singleton.Done;
  RegisterFactory(IdcObservers, TdcObservers).Singleton.Done;
  RegisterFactory(IdcTimer, TdcTimer).Done;

  // Komponenten
  RegisterFactory(IdcMainMenuPanel, TdcMainMenuPanel).Singleton.Done;
  RegisterFactory(IdcTileHeader, TdcTileHeader).Done;
  RegisterFactory(IdcTileView, TdcTileView).Singleton.Done;
  RegisterFactory(IdcListMenu, TdcListMenu).Done;
  RegisterFactory(IdcEasyItem, TdcEasyItem).Done;
  RegisterFactory(IdcTreeview, TdcTreeView).Done;

  Application.CreateForm(TdcMainForm, dcMainForm);

  // Forms & Frames
  RegisterFactory(IdcMainForm, dcMainForm).Singleton.Done;
  RegisterFactory(IdcFrameMenuStudio, TdcFrameMenuStudio).Done;
  RegisterFactory(IdcFrameMenuSettings, TdcFrameMenuSettings).Singleton.Done;

  Application.Run;
Lediglich der Aufruf
Delphi-Quellcode:
Application.CreateForm
"tut" tatsächlich etwas, der Rest wird nur registriert.

Die Deklaration von MainForm ist wie gewohnt, z.B.:
Delphi-Quellcode:
type
  TdcMainForm = class(TBaseForm, IdcMainForm)
Und in einer Interface-Unit deklariere ich:

Delphi-Quellcode:
type
  IdcMainForm = interface(IdcWinControl)
    ['{344DCBC9-1B4B-48C6-B7C4-E5CC329EA658}']
    function BaseForm : TWinControl;
    procedure ApplyStyle;
  end;
  function DI_MainForm : IdcMainForm;
Wobei die Funktion nur kapselt:
Delphi-Quellcode:
function DI_MainForm : IdcMainForm;
begin
  Result := DIService.Get<IdcMainForm>;
end;
Emballo kommt bei mir also nur an 2 Stellen in's Spiel: in der dpr mit
Delphi-Quellcode:
RegisterFactory
und in der Interface-Unit im Funktions-Aufruf mit
Delphi-Quellcode:
DIService.Get<IdcMainForm>
.

Lohn der Mühe ist nun, das ich überall, wo ich die Interface-Unit einbinde, ich mittels Funktionsaufruf auf die Forms usw. zugreifen kann, ohne deren Units einzubinden und mich um die Instanziierung und/oder Freigabe kümmern zu müssen. Das gibt übersichtlicheren Code und keine Cross-Referenzen.

In diesem Beispiel nutze ich mehrere Service-Interfaces (DI_XXX) für das Einblenden eines Frames in das Hauptformular:
Delphi-Quellcode:
  DI_BlendTransition.Start(DI_MainForm.BaseForm);
  try
    DI_Stages.ActiveStageViewIndex := aButton.StageViewIndex;
    DoOnActiveStageItemChanged;
  finally
    DI_BlendTransition.Stop;
  end;
Dabei muss keine Unit für das MainForm (oder die Transition) eingebunden werden, den die "kennen" sich über die Interface-Deklarationen und Emballo liefert mir den Rest und kümmert sich auch (mit Delphi) um die Freigabe ;)

Sorry für die etwas längere Antwort, aber ich wollte nicht nur eine Link posten.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:00 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