Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Probleme mit Indy FTP in eigenem Thread (https://www.delphipraxis.net/129534-probleme-mit-indy-ftp-eigenem-thread.html)

karma 20. Feb 2009 18:42


Probleme mit Indy FTP in eigenem Thread
 
Hallo Leute,
ich hoffe ich bin im richtigen Forum für meine Frage.
Ich habe folgendes Problem: Ich habe in meinem Programm den FTP Client in einen eigenen Thread ausgelagert, damit mein Form nicht hängt während er aktiv ist. Das funktioniert auch prima, allerdings wartet das Programm, wenn ich es beenden möchte, darauf, dass z.B. eine laufende Dateiübertragung fertiggestellt ist. Ich möchte aber, dass beim beenden eventuell laufende Übertragungen abgebrochen werden und sich das Programm sofort beendet.


Außerdem noch zwei andere Dinge: Im Programm gibt es einen Button, der den Download startet. Nach Fertigstellung soll dann ein Windows-Explorerfenster mit dem Download geöffnet. Ich habe das jetzt mit einer Schleife gemacht, die folgendermaßen aussieht:

Delphi-Quellcode:
 while(not ftpload.terminated) do application.ProcessMessages;
Danach kommt dann der Aufruf zum Öffnen des Explorers. Vielleicht als kurzen Hinweis dazu, der Thread zerstört sich nicht selbst, und die boolvariable terminated setze ich im OnTerminate des Threads. Das funktioniert an sich, unschönerweise bewirkt die Schleife aber eine 100% CPU Auslastung. Ich dachte immer, Windows blockiert den Messageloop immer automatisch, damit das nicht passiert?
Ich kann den Explorer-Aufruf leider nicht aus dem Thread machen, weil ich zusätzlich dazu nach dem Download auch noch einen Eintrag in eine Datenbank machen muss. Und ich weiß nicht, ob ich auf eine ZEOS-Connection aus 2 Threads gleichzeitig zugreifen darf.


Zweites Problem ist, man könnte jetzt ja theoretisch ein weiteres Mal auf den Button klicken. Wenn der Thread jetzt noch läuft, soll ein Hinweis kommen, in dem man auswählen kann ob man den Thread beenden möchte, oder ob er weiterlaufen soll. Dazu ist ein MessageDlg drin, der beim Klicken auf Ja die terminate-Prozedur meiner TThread-Klasse aufruft. Leider beendet das aber den Thread nicht x) Ich schätze, hier ist das das gleiche Problem wie wenn man das gesamte Programm beenden möchte. Indy scheint den Thread so zu blockieren, dass er auf nichts anderes mehr reagiert.

Wie kann ich das Problem lösen? Vielen Dank für eure Hilfe!

jfheins 20. Feb 2009 19:47

Re: Probleme mit Indy FTP in eigenem Thread
 
Du hast doch im Thread die IdFTP-Klasse. Prüfe im OnWork of Terminated wahr ist. Wenn dem so ist, solltest du den Download beenden.

Damit kannst du den Thread per ftpload.Terminate; beenden.

Zitat:

Ich kann den Explorer-Aufruf leider nicht aus dem Thread machen,
Wenn du etwas machen möchtest, wenn der Thread beendet wurde brauchst du nicht ständig Application.Processmessages aufrufen (womit du, nebenbei bemerkt, das "warten auf neue Nachrichten" ad absurdum führst, da es nichts anderes ist als Polling)
sondern es müsste ein TThread.Terminated Event geben, in welchem du so etwas machen kannst. Alternativ eine synchronized-Prozedur ;)

karma 20. Feb 2009 21:07

Re: Probleme mit Indy FTP in eigenem Thread
 
Vielen Dank für deine Antwort. Stimmt, an OnWork habe ich gar nicht gedacht ;)
Kann man eigentlich TThread auch vom Hauptthread killen lassen?

Zitat:

Zitat von jfheins
Wenn du etwas machen möchtest, wenn der Thread beendet wurde brauchst du nicht ständig Application.Processmessages aufrufen (womit du, nebenbei bemerkt, das "warten auf neue Nachrichten" ad absurdum führst, da es nichts anderes ist als Polling)
sondern es müsste ein TThread.Terminated Event geben, in welchem du so etwas machen kannst. Alternativ eine synchronized-Prozedur ;)

Ok, dann hab ich das wohl falsch verstanden, ich dachte OnTerminate wird noch aus dem Thread raus aufgerufen.


Zu dem Processmessages hab ich dann aber nochmal ne Frage ;) Ich hatte mir das bis jetzt so vorgestellt, dass ich damit in den Messageloop komme und damit dann weiter Eingaben verarbeiten kann. Wie macht man es denn "richtig", dass, wenn ich jetzt folgendes Beispiel habe:

Ich habe einen Thread, in dem in einer bestimmten Zeit ein Zähler hochgezählt wird. Jetzt möchte ich in meinem Hauptthread eine Abfrage machen, ob der Zähler über einem bestimmten Wert ist und dann an der Stelle weitermachen. Die einzige Lösung die mir da einfällt ist in einer while Schleife immer wieder das Ergebnis zu überprüfen und processmessages aufzurufen, damit das Formular noch reagiert?

jfheins 20. Feb 2009 21:14

Re: Probleme mit Indy FTP in eigenem Thread
 
Zu der letzten Frage: Du kannst im Thread hochzählen, und gucken ob der Wert erreicht ist. Wenn das der Fall ist, rufst du ein Event im Hauptthread auf, in dem das ausgeführt wird, was passieren soll.

Um eine Methode im MainThread auszuführen musst du synchronize() verwenden.

Also z.B.
Delphi-Quellcode:
TMyThread = class(TThread)
i : Integer;
MainForm: TForm1;
// ...

procedure TMyThread.Execute()
begin
while i <= 1000
begin
inc(i)
sleep(50)
if i = 400 then
  synchronize(Ausgabe);
end;
end;

procedure TMyThread.Ausgabe()
begin
MainForm.Label1.Caption := inttostr(i);
// oder
MainForm.DoSomething(i);
// oder
Mainform.DoSomething(Self);
end;

karma 20. Feb 2009 21:46

Re: Probleme mit Indy FTP in eigenem Thread
 
Zitat:

Zitat von jfheins
Zu der letzten Frage: Du kannst im Thread hochzählen, und gucken ob der Wert erreicht ist. Wenn das der Fall ist, rufst du ein Event im Hauptthread auf, in dem das ausgeführt wird, was passieren soll.

Um eine Methode im MainThread auszuführen musst du synchronize() verwenden.

Ah, das ist natürlich gut zu wissen.
Meine Frage ist wohl doch etwas komplizierter, konkret handelt es sich bei dem Programm was ich meine um ein Projekt für die Uni, bei dem es um eine Robotersteuerung geht. Der Roboter wird über eine DLL gesteuert, die nur rudimentäre Befehle zum Bewegen und zur Positionsabfrage mitbringt. Über ein Skriptsystem kann man einen Bewegungsablauf für den Roboter festlegen.
Folgendes Problem: Die Befehle zur Bewegung kehren sofort zum System zurück. Ich will aber, dass der Roboter seine Bewegung erst abschließt und erst anschließend den nächsten ausführt. Bis jetzt habe ich das wie hier hingewurschtelt, und zwar mit einer Schleife, die mit while(RoboterInBewegung) abwartet und währenddessen application.processmessages aufruft, damit das Formular noch reagiert. Wie stelle ich es denn in diesem Fall am geschicktesten an? Die DLL stellt leider keinerlei Callbacks bereit, sodass ich nur abfragen kann, ob sich der Roboter bewegt oder steht. Außerdem soll der Roboter zwischen den einzelnen Bewegungen möglichst nicht stehen, deswegen geht auch kein sleep(50) oder etwas in der Art um die CPU zu schonen. Vielleicht hast du da ja noch eine Idee? ;)

Vielen Dank auf jedenfall für deine Hilfe!

jfheins 20. Feb 2009 22:50

Re: Probleme mit Indy FTP in eigenem Thread
 
Also wenn die DLL nichts in der Richtung bereitstellt, isses natürlich doof.

In diesem Fall würde ich einen Thread benutzen, der die Robotersteuerung übernimmt.

Der kann dann ja z.B. eine Liste haben und wie folgt arbeiten:
Delphi-Quellcode:
procedure TThread.Execute;
begin
  while (not Terminated) and (not CommandQueue.IsEmpty) do
  begin
    Execute(CommandQueue.Pop());
    while Robot.ismoving do Sleep(0);
    // evtl. dem Mainthread den Fortschritt mitteilen
  end;
end;
Das Sleep(0) bewirkt hier, dass die Bearbeitung des Threads unterbrochen wird und andere Prozesse Rechenzeit bekommen (solange du ihn nicht auf Echtzeitpriorität stellst ...)

Das Ausgelagere in den Thread heißt, dass du auf Application.Processmessages verzichten kannst ;)

karma 21. Feb 2009 11:23

Re: Probleme mit Indy FTP in eigenem Thread
 
In der Doku ist leider noch angegeben, dass die DLL nicht Threadsafe ist. Vielleicht mach ich nochmal einen extra Thema zu der Frage auf wenn ich da weiter dran arbeite.

Dir auf jedenfall vielen Dank für eine ausfürliche Hilfe :) :hi:

jfheins 21. Feb 2009 12:35

Re: Probleme mit Indy FTP in eigenem Thread
 
Nicht-Threadsafe heißt, dass du die Funktionen nicht aus 2 verschiedenen Threads ausführen darfst.

Wenn du aber konsequent sämtliche Aufrufe in dem einen Thread machst (also im Hauptthread nichts dergleichen) dann sollte es keine Probleme geben ;)

Es ist im Grunde egal wieviele Threads deine Anwendung hat, wenn die dll nicht thread-safe ist, bedeutet das nur dass höchstens ein Thread auf die DLL zugreifen darf. Welcher das ist darfst du dir aussuchen.

karma 21. Feb 2009 14:54

Re: Probleme mit Indy FTP in eigenem Thread
 
Jo, soviel war mir klar. Leider ist nur im Hauptthread schon ne ganze Menge Code, sodass der Umbau ziemlich aufwändig wäre. Naja muss ich mich vielleicht mal ran machen x)


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:30 Uhr.

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