Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Probleme beim Warten auf 2 Threads (https://www.delphipraxis.net/81392-probleme-beim-warten-auf-2-threads.html)

Panthrax 26. Nov 2006 15:06

Re: Probleme beim Warten auf 2 Threads
 
Hallo Rolf.

Das WaitFor im Hauptthread der Anwendung einzubaue ist nicht sinnvoll. Damit konstruiert du genau das, was du zu vermeiden versuchst - das "Hängenbleiben" deiner Anwendung. Es bleibt also nichts anderes, als die Threads für sich separat arbeiten zu lassen und über eine Ereignisbehandung bzw. Signale zu kommunizieren, um damit eben nicht aktiv zu warten.

Ich möchte daher noch eine andere Läsung vorschlagen: Hast du dir die Delphi-Hilfe dazu einmal angesehen?
  • Warten, bis ein Thread vollständig ausgeführt ist
  • Warten, bis eine Aufgabe ausgeführt ist
Sehr angenehm kann man auch mit Hilfe des OnTermine-Ereignisses arbeiten. Die Delphi-Hilfe gibt folgende Information zum Ereignis OnTerminate:
Zitat:

Das Ereignis OnTerminate wird ausgelöst, sobald die Methode Execute des Threads zurückgekehrt ist, aber bevor der Thread freigegeben wird.
Gruß,
Panthrax.

RWarnecke 26. Nov 2006 15:18

Re: Probleme beim Warten auf 2 Threads
 
Irgendwie klappt das nicht. Denn einer beiden Threads gibt kein True zurück, deshalb bleibt es in der repeat-Schleife hängen.

Zitat:

Zitat von Panthrax
Ich möchte daher noch eine andere Läsung vorschlagen: Hast du dir die Delphi-Hilfe dazu einmal angesehen?
  • Warten, bis ein Thread vollständig ausgeführt ist
  • Warten, bis eine Aufgabe ausgeführt ist
Sehr angenehm kann man auch mit Hilfe des OnTermine-Ereignisses arbeiten. Die Delphi-Hilfe gibt folgende Information zum Ereignis OnTerminate:
Zitat:

Das Ereignis OnTerminate wird ausgelöst, sobald die Methode Execute des Threads zurückgekehrt ist, aber bevor der Thread freigegeben wird.

Ja, ich habe mir die Delphihilfe angesehen und wurde nicht richtig schlau draus. Deshalb habe ich diesen Weg gewählt. Könntest Du mir das bitte anhand meines Beispieles bitte erklären.

C.Schoch 26. Nov 2006 16:37

Re: Probleme beim Warten auf 2 Threads
 
Hi,
Du köntest auch per msgWaitForMultipleObjekts oder WaitForMultipleObjekts auf die Threads warten.

z.B. so:
Delphi-Quellcode:
 
Var
  dwWaitResult : DWORD;
  ThreadArray : array[0..1] of THandle; // Handle Array um auf Threads zu warten
begin
  SearchForCopy := TSearchCopy.Create(false);
  SearchForCopy.FreeOnTerminate := False;
  SearchForDelete := TSearchDelete.Create(false);
  SearchForDelete.FreeOnTerminate := False;
  ThreadArray[0] := SearchForCopy.Handle; // Handle Array füllen
  ThreadArray[1] := SearchForDelete;
  repeat
    // Auf Threads warten
    dwWaitResult := msgWaitforMultipleObjects(length(ThreadArray), ThreadArray, true, 100, QS_ALLINPUT);
    if dwWaitResult <> WAIT_OBJECT_0 then
    begin
       Application.ProcessMessages; // GUI aktualisieren
    end;  
  until dwWaitResult = WAIT_OBJECT_0; // keinen Threads mehr zum warten
end;

Panthrax 26. Nov 2006 16:52

Re: Probleme beim Warten auf 2 Threads
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Rolf.

Ich beschreibe dir hier das Konzept noch einmal etwas genauer. Im Anhang habe ich das Konzept in einem Beispielprojekt gezeigt.

Das Konzept für 2 Threads

Du willst auf die Beendigung zweier Threads warten. Dafür bietet sich das OnTerminate-Ereignis der Klasse TThread an. Die Ereignisbehandlungsroutine braucht die Informationen über
  • den Thread, der abgeschlossen ist
  • den anderen Thread, ob er noch läuft oder nicht.
Nun ist die Sache einfach. Aus der ersten Information kann geschlussfolgert werden, welcher Thread als beendet markiert werden kann. Das kann nicht mithilfe des Threads selbst passieren, da sich dieser nach Beendigung selbst freigibt (FreeOnTerminate = True). Aus der zweiten Information kann geschlussfolgert werden, ob beide Threads damit beendet sind.

Ein Beispiel

Im Beispiel habe ich die Klasse TSleepThread von TThread abgeleitet. Die Aufgabe ist eine bestimmte Zeit zu schlafen. Anschließend ist der Thread beendet. Wie lange geschlafen werden soll wird im Kontruktor angegeben. Ther Parameter CreateSuspended wird an den Kontruktor von TThread weitergereicht. Kurzfassung:
Delphi-Quellcode:
type
  TSleepThread = class(TThread)
  ...

constructor TSleepThread.Create(const Milliseconds: Cardinal; const CreateSuspended: Boolean);
Zum Formular habe ich 2 Variablen (Eigenschaften) hinzugefügt: FThread1 (Thread1) und FThread2 (Thread2). Beide sind von der Klasse TSleepThread. Diese Felder dienen nur zum Merken, ob ein Thread beendet ist (= nil) oder nicht (<> nil). Kurzfassung:
Delphi-Quellcode:
type
  TForm1 = class(TForm)
    private
    FThread1, FThread2: TSleepThread;

    published
    property Thread1: TSleepThread read FSleepThread1 write FSleepThread1;
    property Thread2: TSleepThread read FSleepThread2 write FSleepThread2;
  end;
Die Methode RunThreads initialisiert und startet die Threads. Kurfassung:
Delphi-Quellcode:
procedure RunThreads;
Der Kern liegt in der Methode DoTerminate. Diese wurde den erzeugten Threads dem Ereignis OnTerminate zugewiesen. Neben einer Ausgaberückmeldung setzt diese Methode die Merker Thread1 bzw. Thread2 auf Nil, wenn der zugehörige Thread beendet wurde. Wurden beide abgearbeitet, sind folglich beide Nil. Das ist also der Zustand, nach dem du gesucht hast. Kurzfassung:
Delphi-Quellcode:
procedure TForm1.DoTerminate(Sender: TObject);
begin
  { Merker setzten, dass ein Thread beendet wurde. }
  if Sender = Thread1 then Thread1:=nil else
  if Sender = Thread2 then Thread2:=nil;
  { Fall, dass beide Threads beendet wurden. }
  if not Assigned(Thread1) and not Assigned(Thread2) then...;
end;
Damit ergibt sich also nicht mehr das Problem, dass das Programm "hängt".

Viel Spaß beim ausprobieren mit dem Beispiel. Ich habe es so gestaltet, dass man die "Laufzeit" der Threads einfach in der Oberfläche einstellen kann.

Beispiel erstellt und getestet mit Delphi 7 Enterprise.

Gruß,
Panthrax.

[edit=Panthrax]Glatt den Anhang vergessen... Jetzt ist er dabei.[/edit]

RWarnecke 26. Nov 2006 18:45

Re: Probleme beim Warten auf 2 Threads
 
Ersteinmal ein dickes Danke an Christian, den sein Code funktioniert schon mal, wenn auch nicht ganz mit der Performance, als wenn ich nach dem Aufruf einfach die OnClick-Procedure beende.

Auch ein nochmaliges Danke an Dich Panthrax für Deine ausführliche Beschreibung. Verstehe ich das soweit richtig, dass der Code der nach der Ausführung der beiden Threads ausgeführt werden soll in der DoTerminate-Procedure stehen muss ? Wenn ja, wäre das nicht ganz Sinnvoll, da der Start von den beiden Threads in einer For-Schleife steht.

Panthrax 26. Nov 2006 19:31

Re: Probleme beim Warten auf 2 Threads
 
Schön, dass es dir gefällt.

Ob der Code, der nach Abschluss beider Threads ausgeführt werden soll, in der Ereignisbehandlungsroutine von OnTerminate stehen sollte oder nicht, kann ich nicht beurteilen. Fest steht, dass dort auf diesen Zustand reagiert werden kann. Sei es durch direktes Fortführen des Programmablaufs dort oder durch Anstoßen eines anderen Programmteils.

Ich würde sehr wahrscheinlich eine Routine schreiben, die diese sich anschließenden Aufgaben übernimmt. Um dir jedoch dabei helfen zu können, bäuchte man schon etwas genauere Angaben, als "steht in einer For-Schleife". Es bleibt die Frage: Was hat die For-Schleife damit zu tun?

Gruß,
Panthrax.

RWarnecke 27. Nov 2006 09:49

Re: Probleme beim Warten auf 2 Threads
 
Also, ich habe das ganze mal gestern Abend nach dem Beispiel von Panthrax in mein Programm versucht umzusetzen. Es hat leider nicht funktioniert. Ich muss doch meine ganze Struktur im Programm ändern.


Zitat:

Zitat von C.Schoch
z.B. so:
Delphi-Quellcode:
 
Var
  dwWaitResult : DWORD;
  ThreadArray : array[0..1] of THandle; // Handle Array um auf Threads zu warten
begin
  SearchForCopy := TSearchCopy.Create(false);
  SearchForCopy.FreeOnTerminate := False;
  SearchForDelete := TSearchDelete.Create(false);
  SearchForDelete.FreeOnTerminate := False;
  ThreadArray[0] := SearchForCopy.Handle; // Handle Array füllen
  ThreadArray[1] := SearchForDelete;
  repeat
    // Auf Threads warten
    dwWaitResult := msgWaitforMultipleObjects(length(ThreadArray), ThreadArray, true, 100, QS_ALLINPUT);
    if dwWaitResult <> WAIT_OBJECT_0 then
    begin
       Application.ProcessMessages; // GUI aktualisieren
    end;  
  until dwWaitResult = WAIT_OBJECT_0; // keinen Threads mehr zum warten
end;

Kann man diesen Sourcecode noch irgendwie performanter machen ? Er funktioniert, ist aber grotten langsam.

C.Schoch 27. Nov 2006 21:11

Re: Probleme beim Warten auf 2 Threads
 
Hi,
Warum langsam? bei mir funktionirt der recht schnell und gut. Die Threads sind ja von dieser Funktion unabhängig.
Solltest du allerdings aus dem Thread direkt auf ein Element der GUI zugreifen (Auch über Syncronize) dann dauert es natürlich bis zu 100 ms bis dein Code fortgesetzt wird.
Ich vermute du verwendest diesen Code in CopyandSync und aktualisierst bei jedem Durchlauf von FindFirst / FindNext den Text deiner Statusbar so wird höchstens alle 100ms eine Datei gefunden!
Das Stichwort heist hier Messages oder du setzt eine Variable im Thread/Hauptprogramm den du vor Application.ProcessMessages ausließt und in Statusbar schreibst.

RWarnecke 10. Dez 2006 12:35

Re: Probleme beim Warten auf 2 Threads
 
Zitat:

Zitat von C.Schoch
Hi,
Warum langsam? bei mir funktionirt der recht schnell und gut. Die Threads sind ja von dieser Funktion unabhängig.
Solltest du allerdings aus dem Thread direkt auf ein Element der GUI zugreifen (Auch über Syncronize) dann dauert es natürlich bis zu 100 ms bis dein Code fortgesetzt wird.

Ich habe es jetzt mal probiert, wenn ich den Wert von 100ms runtersetze auf 5. Dann geht es schneller.
Zitat:

Zitat von C.Schoch
Ich vermute du verwendest diesen Code in CopyandSync und aktualisierst bei jedem Durchlauf von FindFirst / FindNext den Text deiner Statusbar so wird höchstens alle 100ms eine Datei gefunden!

Damit liegst Du richtig. Ich setze diesen Code in meinem Projekt CopyAndSync ein. Das auslesen der Dateien erfolgt aber nicht über FindFirst / FindNext, sondern über eine Unit aus dem Synchronisationstool Pegasus von Luckie.
Zitat:

Zitat von C.Schoch
Das Stichwort heist hier Messages oder du setzt eine Variable im Thread/Hauptprogramm den du vor Application.ProcessMessages ausließt und in Statusbar schreibst.

Das mit der Variable oder Message verstehe ich da noch nicht ganz.

C.Schoch 11. Dez 2006 00:28

Re: Probleme beim Warten auf 2 Threads
 
Zitat:

Zitat von RWarnecke
Ich habe es jetzt mal probiert, wenn ich den Wert von 100ms runtersetze auf 5. Dann geht es schneller.

Was meine Vermutung bekräftigt, dass du aus den Threads heraus auf die GUI zugreifst.

Zitat:

Zitat von RWarnecke
Damit liegst Du richtig. Ich setze diesen Code in meinem Projekt CopyAndSync ein. Das auslesen der Dateien erfolgt aber nicht über FindFirst / FindNext, sondern über eine Unit aus dem Synchronisationstool Pegasus von Luckie.

Auch der benutzt Find First/Next

Zitat:

Zitat von RWarnecke
Das mit der Variable oder Message verstehe ich da noch nicht ganz.

Ich übergebe dem Thread einen Pointer auf eine in meiner Hauptanwendung definierten String den ich dann einfach im Thread mit Meldungen fülle:
Delphi-Quellcode:
  // Im Hauptprog
  SearchForCopy := TSearchCopy.Create(false);
  SearchForCopy.FreeOnTerminate := False;
  SearchForCopy.Message := @MessageString;
Delphi-Quellcode:
  //im Thread
  Message^ := 'Kopiere Datei xyz';
Und setze dann diese Meldung alle 100ms in meine Anzeige:
if dwWaitResult <> WAIT_OBJECT_0 then
begin
Label1.Caption := MessageString;
Application.ProcessMessages; // GUI aktualisieren
end;
Frei getippt!

p.s.: Es scheint ein Problem mit meinem Code (vor allem mit dem fWaitAll) zu geben schau mal hier:msgWait... sollte aber nicht weiter schlimm sein.
Damit gehts.
Delphi-Quellcode:
dwWaitResult := WaitforMultipleObjects(length(ThreadArray), @ThreadArray, true, 100);


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:03 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz