Delphi-PRAXiS
Seite 2 von 4     12 34      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Cross-Platform-Entwicklung (https://www.delphipraxis.net/91-cross-platform-entwicklung/)
-   -   Form reagiert nicht während idftp1.get trotz application.processmessages (https://www.delphipraxis.net/181068-form-reagiert-nicht-waehrend-idftp1-get-trotz-application-processmessages.html)

gee21 13. Jul 2014 12:45

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Zitat:

Zitat von Luckie (Beitrag 1265366)
Nur kurz zur Erklärung, damit beim nächsten mal nicht wieder gefragt wird. Die Methode Get kehrt erst zurück, wenn sie fertig ist. Da kannst da du vor und danach so oft die Messageque abarbeiten wie du willst, die Methode wird dir trotzdem dein Programm blockieren. Wenn dann müsste das Apploication.ProcessMessages in die Get Methode rein. Da du aber da keinen Einfluss drauf hast, hilft es nur die Methode in einen separaten Thread auszulagern.


Vielen Dank für die Info.
Ich versuche ja den idftp1.get Befehl in einen Thread auszulagern. aber ich mache noch was falsch und weiss nicht genau was.

Thread:
Delphi-Quellcode:
unit threadtest;

interface

uses
     System.Classes, f1diashow, System.SysUtils;

type
   geetest = class(TThread)
   protected
     procedure Execute; override;
   end;


implementation


procedure geetest.Execute;
begin
   NameThreadForDebugging('threadgee');
   { Place thread code here }

   form1.idftp1.get(filelistFINAL[i4],gethomepath+'/Temp/'+filelistFINAL[i4], true);
end;

end.

Versuche den Thread auf Form1 so zu starten:
Delphi-Quellcode:
 geetest.Create(False);
Erhalte dann folgendes Fehler:(Access Violation at Adress 61B3D93C, accessing adress 00000000)


Aber ich habe bis jetzt noch nie mit Threads gearbeitet und verstehe es irgendwie noch nicht ganz wie das funktioniert / was ich falsch mache.

Sir Rufo 13. Jul 2014 13:28

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Wenn du einen Thread baust, dann musst du den so bauen, dass der komplett autark laufen kann.
Wenn ein Thread autark ist, dann kann man diesen Thread auch n-fach parallel laufen lassen, ohne dass es zu Störungen kommt.

Ein Beispiel aus dem Leben:

Jedes Auto ist ein eigener Thread und jedes Auto kann völlig unabhängig voneinander fahren.

Wenn ich deine Umsetzung jetzt mal auf die Autos übertrage, dann hast du einen Motor (TIdFTP) und alle Autos benutzen diesen Motor. Irgendwie uncool :)

gee21 13. Jul 2014 14:14

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Ich danke euch allen für eure Hilfe und Geduld.

Jetzt funktioniert es :-D

Sir Rufo 13. Jul 2014 14:20

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Generell empfiehlt es sich bei der Programmierung immer zu überlegen, wie das im realen Leben funktionieren würde.

Statt eine Datei per FTP zu laden stellen wir uns vor, wir wollen etwas besorgt haben. Eine Gurke (Datei) aus dem Supermarkt (FTP-Server). Da wir selber (GUI) gerade am telefonieren sind (Interaktion mit dem User) und den Teilnehmer nicht mit Geräuschlosigkeit verunsichern wollen (Anwendung reagiert nicht) schicken wir jemand Anderen (Thread) los.

Dieser Andere (Thread) braucht nun bevor er losgeht alle notwendigen Informationen um die Aufgabe zu erledigen:
  • Was soll besorgt werden? Eine Gurke (Datei foo.txt)
  • Wo soll das besorgt werden? Supermarkt gleich um die Ecke (ftp.foo.com)
  • Womit soll er kaufen? Mit Geld (Benutzername, Kennwort)
Diese Informationen schreibe ich ihm auf einen Zettel und gebe ihm den Zettel mit (man beachte, dass dieser Zettel nun ausserhalb des eigenen Zugriffs ist).

Irgendwann kommt derjenige dann wieder und hat, wenn nichts schief gelaufen ist, die Gurke (Datei) dabei.

Ab jetzt kann ich auf die Gurke (Datei) zugreifen.

Läuft etwas schief, dann kann ich auf die Gurke (Datei) natürlich nicht zugreifen:
  • Geld hat nicht ausgereicht (Zugangsdaten falsch)
  • Gurken waren aus (Datei nicht gefunden)
  • Der Supermarkt ist zur Zeit geschlossen (Server nicht gefunden)
  • ...

Weitere Betrachtungen:

Wenn ich über den aktuellen Zustand informiert werden möchte, dann muss der Andere (Thread) mich eben fortlaufend anrufen und mich informieren. Da ich aber am telefonieren und nicht unhöflich bin, nehme ich diesen Anruf nicht mitten im Satz an, sondern entschuldige mich kurz, nehme den Anruf und damit die Information an, und lege wieder auf (Synchronize).

Wenn ich schon sehr modern bin, dann lasse ich mir einfach eine SMS schicken und schaue mir wenn ich Zeit habe die Information an (Queue).

Meinen Gegenüber kann ich dann immer wieder über den Fortschritt unterrichten (Progressbar aktualisieren).

Abbruch des Auftrags:

Um den Auftrag abzubrechen, schicke ich dem Anderen (Thread) eine SMS (Event), dass ich die Gurke (Datei) nicht mehr benötige.
  • Ist er noch auf dem Weg dorthin, kehrt er einfach wieder zurück (vor der FTP-Verbindung)
  • Ist er schon im Supermarkt (Verbindung zum FTP-Server hergestellt, Datei wird geladen), dann einfach alles wieder weglegen und zurückkehren (Verbindung trennen und die bis jetzt geladenen Daten verwerfen)
  • Hat er die Gurke schon gekauft, dann die Gurke wegwerfen (Datei wieder löschen)

nuclearping 13. Jul 2014 14:27

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Zitat:

Zitat von gee21 (Beitrag 1265368)
Delphi-Quellcode:
procedure geetest.Execute;
begin
   NameThreadForDebugging('threadgee');
   { Place thread code here }

   form1.idftp1.get(filelistFINAL[i4],gethomepath+'/Temp/'+filelistFINAL[i4], true);
end;

Du darfst
Delphi-Quellcode:
Form1.IdFtp1.Get()
NICHT in deinem Thread aufrufen, weil du sonst die Funktion im Hauptthread der Anwendung aufrufst (
Delphi-Quellcode:
TForm1
) und diese dort auch ausgeführt wird. Das heisst dass dein Thread in dem Moment garkeinen Sinn mehr hat.

Was du in etwa machen musst:
Delphi-Quellcode:
unit threadtest;

interface

uses
  System.Classes, System.SysUtils,

  // Indy Units
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  IdExplicitTLSClientServerBase, IdFTP;

type
   TThreadTest = class(TThread)
     constructor Create(AFileListFinal: TStringList);
     destructor Destroy; override;
   protected
     FIdFtp: TIdFTP;
     FConnected: Boolean;
     FDownloading: Boolean;
     FFileList: TStringList;
     procedure Execute; override;

     procedure AfterGet(Sender: TObject; Stream: TStream);
     procedure Connected(Sender: TObject);
   public
   end;

implementation

function GetHomePath: String;
begin
  // Die Funktion darf ebenso KEINEN Zugriff auf Komponenten von Form1 oder sowas nehmen.
  Result := '/var/www/...';
end;

procedure TThreadTest.Execute;
var
  i4: Integer;
begin
   NameThreadForDebugging('threadgee');

   FConnected := FALSE;
   FDownloading := FALSE;
   i4 := 4;
   FIdFtp.Connect;

   while not Terminated do
     begin
       Sleep(1);
       if not FConnected then
         Continue;

       if not FDownloading then
         begin
           FDownloading := TRUE;
           Sleep(500);

           FIdFtp.Get(FFileList[i4], GetHomePath + '/Temp/' + FFileList[i4], TRUE);
         end;
     end;
end;

constructor TThreadTest.Create(AFileListFinal: TStringList);
begin
  FFileList := AFileListFinal;
  FIdFtp := TIdFTP.Create(nil);
  with FIdFtp do
    begin
      Host := '...';
      Username := 'lieschen';
      Password := 'purzel123';
      OnConnected := Connected;
      OnAfterGet := AfterGet;
    end;

  inherited Create(FALSE);
  FreeOnTerminate := TRUE;
end;

destructor TThreadTest.Destroy;
begin
  FIdFtp.Free;
end;

procedure TThreadTest.Connected(Sender: TObject);
begin
  FConnected := TRUE;
end;

procedure TThreadTest.AfterGet(Sender: TObject; Stream: TStream);
begin
  Sleep(500);
  FIdFtp.Disconnect;
  FConnected := FALSE;

  Terminate;
end;

end.
Aufruf in deiner
Delphi-Quellcode:
Form1
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  TTestThread.Create(filelistFINAL);
end;
Ungetestet und ausm Kopf. Also kopier das jetzt nicht einfach 1:1 und sag dann "Es geht nicht!!!111". Da krieg ich schlechte Laune. :mrgreen:

Es soll nur das Prinzip verdeutlichen. Das ist ungefähr das, was Sir Rufo mit "autark" meint. Dein Thread darf sich nicht (ohne weiteres) von Inhalten aus Form1 bedienen oder Funktionen darauf aufrufen.

Dejan Vu 13. Jul 2014 14:52

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Blöde Frage: Es ist korrekt, die FTP-Komponente im Hauptthread zu erzeugen (im Konstruktor), das 'Get' aber im Kontext des Threads aufzurufen? Ich würde die Komponente als lokale Variable im Execute erzeugen, verwenden und wieder freigeben (mit Try-Finally natürlich).

nuclearping 13. Jul 2014 15:28

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Zitat:

Zitat von Dejan Vu (Beitrag 1265383)
Blöde Frage: Es ist korrekt, die FTP-Komponente im Hauptthread zu erzeugen (im Konstruktor), das 'Get' aber im Kontext des Threads aufzurufen?

Das macht er ja. Und es funktioniert nicht. :stupid:

gee21 13. Jul 2014 17:19

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Hey Leute ihr seit super.

Ich glaube bei mir hat es zuerst nicht geklappt da ich nur die "idftp1.get" Zeile im Thread hatte und jetzt wo ich die ganze Schlaufe in den Thread genommen habe, läuft es perfekt und sehr sehr schnell.


Obwohl ich im Moment im Thread immer noch:
Delphi-Quellcode:
Form1.idftp1.get...
habe

Delphi-Quellcode:
form1.idftp1.get(filelistFINAL[i4],gethomepath+'/Temp/'+filelistFINAL[i4], true);
Funktioniert aber so gut, das ich mich kaum getraue was zu ändern :-D

Sir Rufo 13. Jul 2014 17:32

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Nun dann solltest du beten, dass du die Form niemals umbenennst und den Thread nur einmal erzeugst ...

gee21 13. Jul 2014 17:42

AW: Form reagiert nicht während idftp1.get trotz application.processmessages
 
Zitat:

Zitat von Sir Rufo (Beitrag 1265394)
Nun dann solltest du beten, dass du die Form niemals umbenennst und den Thread nur einmal erzeugst ...

Ok, vielleicht ändere ich es dann doch noch lieber. Danke :thumb:

Aber ich habe jetzt den Thread 3-4 mal im selben Programmstart aufgerufen und es klappt tiptop. :?:


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

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