Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Laden von HTML-Seiten mit Multithread (https://www.delphipraxis.net/85416-laden-von-html-seiten-mit-multithread.html)

Gravitar 30. Jan 2007 14:50


Laden von HTML-Seiten mit Multithread
 
Hi,

ich stehe im Moment vor folgendem Problem:

1. Ich habe eine Liste von ca. 100 Web-URL's
2. Diese URL's möchte ich unter Ausnutzung der kompletten DSL-Bandbreite laden und auswerten.

Also dachte ich mir, da könnte man doch mit Multithreading (100 Threads parallel) arbeiten.

Leider verstehe ich nicht, wie ich beim Aufruf eines Threads Parameter mitgeben kann (URL, Index innerhalb der Tabelle).

Hoffe, das Problem ist schon gelöst

Gruß, Andreas

muRR 30. Jan 2007 15:12

Re: Laden von HTML-Seiten mit Multithread
 
Hallo,
sicherlich gibt es Lösungen dafür. Die Suche hilft da gewiss weiter: Hier im Forum suchenthread AND parameter
Falls du dennoch nicht fündig werden solltest, melde dich doch nochmal.

Pfoto 30. Jan 2007 15:46

Re: Laden von HTML-Seiten mit Multithread
 
Hallo!

ich kann auch hier die Einführung einer Jobliste mit
einer beliebig festlegbaren Menge an Threads empfehlen.

Denn du willst evtl. nach und nach immer neue URLs
ausgewertet bekommen. Da wäre es unpassend, stets immer
neue Threads für jede weitere URL zu erstellen.
Da bricht dir ja irgendwann das System zusammen.

Lies mal diese Beiträge und schau
dir das Projekt im letzten Beitrag an (ZIP-Datei)

Mach dir am Besten auch schon Gedanken darüber, wie
du die ausgewerteten Daten weiterverarbeiten willst,
denn da fängt die Synchronisation an, damit die Daten
z.B. geordnet in eine Datenbank oder ein File-Record
gelagen.



Gruß
Pfoto

Gravitar 4. Feb 2007 11:27

Re: Laden von HTML-Seiten mit Multithread
 
Hi,

ich habe mich jetzt (dank der Tipps) eingelesen. Allerdings fand ich die beschriebenen Lösungen für mein Problem als zu groß.

Also, frisch ans Werk und das (zumindest geglaubt) Verstandene umgesetzt!

Die Definition eines Threads gelang mir ja noch:

Delphi-Quellcode:
TMyThread = class(TThread) protected
  procedure execute; override;
Danach dann die Aufruffunktion:

Delphi-Quellcode:
for x := 0 to 10 do
   begin
     MyThread := TmyThread.create(True);
     mythread.resume;
     sleep(10);
   end;
Aber hier ging es schon los.
In der Beschreibung steht, dass nach .create die Funktion .execute ausgeführt wird. Wird sie aber nicht! Nur wenn .resume genutzt wird, scheint der .execute-Block ausgeführt zu werden. Warum auch immer (eigentlich ist resume für das Wideraufsetzen nach einem suspend).

Doch danach wird es noch schlimmer (hier der execute-Block):

Delphi-Quellcode:
procedure TMyThread.Execute;
var web : string;
begin
  web := UTF8ToWideString(form1.IdHTTP1.Get(form1.ListView1.Items[x].SubItems.Strings[4]));
  form1.ListView1.Items[x].SubItems.Strings[4] := web;
end;
Die Variable X ist global definiert. Danach soll sich execute also aus einem ListView aus der x-ten Zeile eine URL holen und über idhttp1.get den HTML-Inhalt besorgen.

Mit einem Thread funktioniert das sogar.

Ab 2 Threads gibt es eine Exception: Class EIdSocketError with Message 'Socket Error #10057 Socket is not connected' :gruebel:

Jetzt kann es natürlich sein, dass die idHTTP1.get überhaupt nicht in der Lage ist multithreaded ausgeführt zu werden. Dann kann ich mir natürlich einen Wolf schreiben!

Andererseits wurden in den Dokumenten häufig davon gesprochen, eine Funktion trheadsicher zu machen.

Tja, wenn jetzt jemand Hilfe weiss, ich könnte Hilfe vertragen.

Gruß, Andreas

Antigo 4. Feb 2007 11:34

Re: Laden von HTML-Seiten mit Multithread
 
Du wirst für jede Webseite die du downloaden möchtest eine eigene Instanz von idHttp erstellen müssen.

Ich hab auch noch nicht oft mit Threads gearbeitet, aber ich meine man müsste Zugriffe auf Objekte und Komponenten ausserhalb des Threads in eine seperate Funktion auslagern und über Synchronize(DeineProzedur); aufrufen. Dazu steht aber meine ich auch einiges in der Delphi Hilfe.

igel457 4. Feb 2007 11:36

Re: Laden von HTML-Seiten mit Multithread
 
Deklariere doch für jeden Thread ein eigenes IdHTTP. Wie soll das denn funktionieren, wenn hundert Threads gleichzeitig auf ein IdHTTP zugreifen?

Pfoto 4. Feb 2007 11:44

Re: Laden von HTML-Seiten mit Multithread
 
Hi!

zu "resume"

der Thread wird ja bereits "supended" kreiert:
Delphi-Quellcode:
MyThread := TmyThread.create(True);
Das tust du in diesem Fall mit "True".


So wie du innerhalb deiner Execute-Methode auf
die Form zugreifst, muss es früher oder später knallen.

Zu verhindern, dass mehrere Threads (und evtl. die
Hauptanwendung) im selben Moment auf die VCLs einer Form
oder anderere Ressourcen zugreifen, meint man mit synchronisieren.

Die Werte aus deiner Liste musst du dir also so beschaffen,
dass es nicht zu Kollisionen kommen kann.

Dazu war auch das Beispiel mit der Jobliste gut, auf das
ich weiter oben verlinkt hatte.
Siehe dir dazu das Objekt "TCriticalSection" an.
Es lässt z.B. in den aufgeführten Funktionen jeweils nur
1 Thread den darin enthaltenen Code ausführen -- die anderen
müssen solange draußen bleiben.


Um Daten mit den Komponenten auf deiner Form zu synchronisieren
gibt es für das Thread-Objekt die Methode Synchronize.
Schau dir dazu auch mal das Beispiel im Delphi Demo-Verzeichnis an.

Diese Dinge müsstest du also bei der Implementierung deiner
Threads berücksichtigen. Schau dich doch nochmal im Forum um;
gerade über das Thema Up-Downloads innerhalb eines Threads gibt es
hier schon eine Menge.


Gruß
Pfoto

Gravitar 4. Feb 2007 11:47

Re: Laden von HTML-Seiten mit Multithread
 
Hi igel457,

Zitat:

Deklariere doch für jeden Thread ein eigenes IdHTTP. Wie soll das denn funktionieren, wenn hundert Threads gleichzeitig auf ein IdHTTP zugreifen?
Aha, wie mache ich denn sowas?

Gruß, Andreas

Pfoto 4. Feb 2007 11:50

Re: Laden von HTML-Seiten mit Multithread
 
Zitat:

Zitat von Gravitar
Hi igel457,

Zitat:

Deklariere doch für jeden Thread ein eigenes IdHTTP. Wie soll das denn funktionieren, wenn hundert Threads gleichzeitig auf ein IdHTTP zugreifen?
Aha, wie mache ich denn sowas?

Gruß, Andreas


Delphi-Quellcode:
TMyThread = class(TThread)
private
  fIdHTTP: TIdHTTP; <-- So!
public
  procedure execute; override;
end;
Edit: Schreibfehler ausgebessert (TIdHTTP)

Sie mir nicht böse wenn ich das sage,
aber beschäftige dich doch zunächst mit den Grundlagen der OOP
bevor du eine Multithreading-Anwendung schreiben willst. ;-)

Gruß
Pfoto

marabu 4. Feb 2007 12:10

Re: Laden von HTML-Seiten mit Multithread
 
Hallo,

man beachte den Schreibfehler:

Delphi-Quellcode:
TMyThread = class(TThread)
private
  fIdHTTP: IdHTTP; <-- So!
public
  procedure execute; override;
end;
Der Typ heißt TIdHTTP - er ist in der Unit idHTTP vereinbart.

Freundliche Grüße

Gravitar 4. Feb 2007 12:59

Re: Laden von HTML-Seiten mit Multithread
 
Hi Pfoto,

dein Tipp

Delphi-Quellcode:
TMyThread = class(TThread)
private
  fIdHTTP: IdHTTP; <-- So! 
public
  procedure execute; override;
end;
funktioniert nicht. Müsste es nach OOP nicht fIdHTTP : TIdHTTP heissen? :wink:

Egal, funktioniert auch nicht, da nach Aufruf in der execute eine Acess-violation in Modul irgendwas auftaucht.

Gruß, Andreas

Antigo 4. Feb 2007 14:12

Re: Laden von HTML-Seiten mit Multithread
 
Acces Violation.. hört sich so an als hättest du das idHTTP Objekt nicht instanziert.

Gravitar 4. Feb 2007 15:24

Re: Laden von HTML-Seiten mit Multithread
 
Hi Antigo,

so, jetzt habe ich auch noch das IdHTTP-Objekt instanziiert.

Der Source sieht jetzt so aus:

Delphi-Quellcode:
TMyThread = class(TThread)
private
  fIdHTTP: TIdHTTP;
public
  procedure execute; override;
end;

...

procedure TMyThread.Execute;
var web : string;
    myidhttp : TIdHTTP;
begin
  myidhttp := tIdHTTP.Create();
  web := UTF8ToWideString(MyIdHTTP.get(form1.ListView1.Items[x].SubItems.Strings[4]));
  form1.ListView1.Items[x].SubItems.Strings[4] := web;
  myidhttp.Free;
end;
Ich gebe ja zu, dass ich mit dieser oop-Materie bisher wenig zu tun hatte, aber wozu habe ich jetzt eigentlich fIdHTTP deklariert?

Das Instanziieren habe ich über TIdHTTP vorgenommen (fidHTTP habe ich auch mal versucht, führt aber sofort zu einem Crash).

Mittlerweile gelingt es mir bis zur 5 Threads zu starten. Komischerweise bekomme ich aber nur 1 Ergebnis zurück (in Zeile 6!

Wenn ich 10 Threads starte, gibts wieder eine Access-Violation! :wall:

Das ganze kann doch so schwer nicht sein oder muss man dafür tatsächlich komplett OOP lernen?

Gruß, Andreas

Gravitar 6. Feb 2007 07:19

Re: Laden von HTML-Seiten mit Multithread
 
Hi,

mittlerweile klappt das Multithreading mit 6 parallelen Threads ganz gut.

Die Definition von fIdHTTP habe ich einfach weggelassen.

Das ich nur 1 Ergebnis zurückbekam lag offenbar an der globalen Variablen X, die nach Abschluß von idHTTP.get bereits auf 6 hochgezählt war und dann nur dorthin zurücklieferte.

Also habe ich einfach die globale Variable in eine lokale kopiert (schöner wäre es natürlich mit einer Variablenübergabe in Execute, aber ich habe keine Ahnung, wie das geht).

So, nun stellen sich "nur" noch die Frage:

Wieso gibt es eine Access-Violation, wenn mehr als 6 Threads gestartet werden? Windows verkraftet doch eine ganze Menge (>450 Threads bei ruhendem System).

Gruß, Andreas

Klaus01 6. Feb 2007 08:48

Re: Laden von HTML-Seiten mit Multithread
 
Vielleicht solltest Du den Zugriff auf VCL Elemente, wie die ListView, synchronisieren.
Ein Beispiel wie das geht findest Du hier -> http://greatis.com/delphicb/tips/lib...nchthread.html

Grüße
Klaus

Gravitar 8. Feb 2007 17:28

Re: Laden von HTML-Seiten mit Multithread
 
Hi,

Danke für die Tipps. Inzwischen habe ich nahezu alle umgesetzt. Der Source sieht jetzt so aus:

Delphi-Quellcode:
TMyThread = class(TThread)
private
  fIdHTTP: TIdHTTP;
  i : integer;
  web : string;
protected
  procedure GetResult;
  procedure execute; override;
public
end;

...

procedure tmythread.GetResult;
begin
  web := UTF8ToWideString(web);
  form1.ListView1.Items[i].SubItems.strings[4] := web;
end;

procedure TMyThread.Execute;
begin
  i := x;
  fIdHTTP := TIdHTTP.Create(nil);
  web := fIdHTTP.get(form1.ListView1.Items[i].SubItems.Strings[4]);
  Synchronize(GetResult);
end;
X und Web sind global definiert und dienen zur Übergabe.

Bei mehr als 10 Threads parallel kommt jetzt die Fehlermeldung "Socket Error #10060 Connection timed out".

Das hört sich jetzt nicht mehr nach einer Access-Violation an.

Weiss jemand Rat?

Gruß, Andreas

Pfoto 8. Feb 2007 17:39

Re: Laden von HTML-Seiten mit Multithread
 
Hi!

vielleicht ist die Wartezeit eines Threads beim
Zugreifen auf eine Seite nun einfach überschritten,
weil er aufgrund der hohen Auslastung nicht
zum Zuge kommt.


Gruß
Pfoto

Gravitar 8. Feb 2007 22:19

Re: Laden von HTML-Seiten mit Multithread
 
Hi,

so, dass Problem ist gelöst! Über 60 parallele HTTP.Get's werden ohne Fehler ausgeführt.

Erstaunlicherweise funktioniert der schon gezeigte Source ohne jede Änderung, wenn man nur in der Aufrufschleife ein "Application.Processmessages" einfügt! (habe ich eigentlich nur gemacht, um die Aktualisierung des ListView hinzubekommen.

Hier nun die komplette Lösung (für diejenigen, die ähnliche Probleme lösen müssen):


Delphi-Quellcode:
{Definition}
TMyThread = class(TThread)
private
  fIdHTTP: TIdHTTP;
  i : integer;
  web : string;
protected
  procedure GetResult;
  procedure execute; override;
public
end;

{Ergebniss des Threads auswerten}
procedure tmythread.GetResult;
begin
  web := UTF8ToWideString(web);
  form1.ListView1.Items[i].SubItems.strings[4] := web;
  fidHTTP.free;
end;

{Thread ausführen}
procedure TMyThread.Execute;
begin
  i := x;
  fIdHTTP := TIdHTTP.Create(nil);
  web := fIdHTTP.get(form1.ListView1.Items[i].SubItems.Strings[4]);
  Synchronize(GetResult);
end;

{Je Zeile innerhalb der Listview wird ein Thread gestartet}
for x := 0 to listview1.Items.count-1 do
begin
  inc(threadcount);
  MyThread := TmyThread.create(true);
  MyThread.freeonterminate := true;
  mythread.Resume;
  sleep(10);
  application.ProcessMessages;
end;
Das wars auch schon.

Man beachte die application.processmessages in der Aufrufschleife. Wenn diese weggelassen wird, kommt es wieder zu den genannten Fehlern (..Time out...).

Wer kann erklären, warum das so ist?

Viele Grüße und nochmals Danke für die Hilfe

Andreas

marabu 9. Feb 2007 08:24

Re: Laden von HTML-Seiten mit Multithread
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Andreas,

Zitat:

Zitat von Gravitar
... Man beachte die application.processmessages in der Aufrufschleife. Wenn diese weggelassen wird, kommt es wieder zu den genannten Fehlern (..Time out...). Wer kann erklären, warum das so ist? ...

durch die Verwendung von Synchronize() wartet das Programm auf den Eintritt des VCL MainThread in die Idle-Routine. Ohne Application.ProcessMessages() passiert das frühestens nach dem Verlassen deiner Schleife.

Deine Klasse TMyThread könnte ich mir auch ohne die feste Kopplung an die Benutzerschnittstelle vorstellen. Der Code im Anhang ist allerdings nicht getestet.

Grüße vom marabu

Gravitar 9. Feb 2007 10:40

Re: Laden von HTML-Seiten mit Multithread
 
Hallo Marabu,

Zitat:

Zitat von marabu
durch die Verwendung von Synchronize() wartet das Programm auf den Eintritt des VCL MainThread in die Idle-Routine. Ohne Application.ProcessMessages() passiert das frühestens nach dem Verlassen deiner Schleife.

Aha, aber warum wird dann der Fehler zurückgemeldet? :gruebel: Nach Verlassen der Schleife wäre das doch auch o.k.

Zitat:

Zitat von marabu
Deine Klasse TMyThread könnte ich mir auch ohne die feste Kopplung an die Benutzerschnittstelle vorstellen. Der Code im Anhang ist allerdings nicht getestet.

Oh, fein. Ich schau mir das mal an und probiere es heute Abend aus. Danke für den Source (das Thema constructor scheint eine Antwort auf meinen ersten Eintrag zu sein (Parameterübergabe) :-D

Gruß, Andreas


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