Autor |
Beitrag |
jackie05
      
Beiträge: 357
|
Verfasst: Di 07.07.09 00:00
Hallo,
ich verstehe den Umgang mit Threads nicht so gut, ich habe hier im Forum nach Tutorials gesucht und bin auch fündig geworden, jedoch verstehe ich sie nicht so ganz.
Was ich machen möchte:
Ich möchte über IdHTTP mehrere Dateien gleichzeitig Downloaden und ich weiss nicht wie ich das mit Threads hinbekomme.
z.B. erstelle ich einen neuen Thread und übergebe den Downloadlink, Speicherort und die ID, um später die einzelnen Threads ansprechen zu können.
Wie mache ich das am einfachsten?
Ich bedanke mich schonmal im Voraus.
|
|
Niko S.
      
Beiträge: 566
Erhaltene Danke: 10
Win 7, Ubuntu
Lazarus, Turbo Delphi, Delphu 7 PE
|
Verfasst: Di 07.07.09 07:23
Wenn du dich nicht so gut mit Threads auskennst solltest du das hier mal probieren:
www.delphi-treff.de/...word_list[0]=threads
Ich selbst habe bis jetzt nur einmal mit nem Thread mit der selben verbindung wie du gearbeitet.
Es ist ganz nützlich, aber man muss auf einiges achten.
|
|
jackie05 
      
Beiträge: 357
|
Verfasst: Di 07.07.09 12:39
Vielen Dank.
Auf dieser Seite war ich auch schon bereits und habe alles durchgelesen.
Ich habe mal folgendes versucht:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, StdCtrls, Gauges;
type TForm1 = class(TForm) Button1: TButton; Gauge1: TGauge; Label4: TLabel; procedure Button1Click(Sender: TObject); private public end;
type TMyThread = class(TThread) protected procedure Execute; override; private HTTP: TIdHTTP; fs: TFileStream; MinKB,MaxKB: integer; procedure HTTPWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer); procedure HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer); end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TMyThread.Execute; begin HTTP := TIdHTTP.Create(nil); HTTP.HandleRedirects := true;
fs := TFileStream.Create('test.rar',fmCreate); HTTP.Get('http://www.meineseite.de/test.rar',fs);
HTTP.Free; fs.Free; end;
procedure TMyThread.HTTPWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer); begin MinKB := AWorkCount; Form1.Gauge1.Progress := MinKB; Form1.Label4.Caption := 'Download: '+IntToStr(MinKB div 1024)+' / '+IntToStr(MaxKB div 1024)+' KB'; Application.ProcessMessages; end;
procedure TMyThread.HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer); begin MaxKB := AWorkCountMax; Form1.Gauge1.MaxValue := MaxKB; Form1.Label4.Caption := 'Download: 0 / '+IntToStr(MaxKB div 1024)+' KB'; end;
procedure TForm1.Button1Click(Sender: TObject); var Thread: TMyThread; begin Thread := TMyThread.Create(false); end;
end. |
Das herunterladen von Dateien funktioniert bereits, nur die beiden funktionen (TMyThread.IdHTTP1Work und TMyThread.IdHTTP1WorkBegin) werden irgendwie nicht ausgeführt, wenn ich den Thread starte um eine Datei herunterzuladen.
Sry, ich arbeite noch nicht solange mit Threads.
Was habe ich in meinem Code falsch gemacht?
Ich bedanke mich schonmal für die Hilfe.
MfG
|
|
tif
      
Beiträge: 46
Erhaltene Danke: 1
Winxxx
TP, BP, Delphi 1 - 2009
|
Verfasst: Di 07.07.09 12:51
Du musst die Eventhandler noch der HTTP - Komponente zuweisen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TMyThread.Execute; begin HTTP := TIdHTTP.Create(nil); HTTP.HandleRedirects := true; HTTP.OnWork=HTTPWork; HTTP.OnWorkBegin:=HTTPWorkBegin; HTTP.fs := TFileStream.Create('test.rar',fmCreate); HTTP.Get('http://www.meineseite.de/test.rar',fs); HTTP.Free; fs.Free; end; |
|
|
jackie05 
      
Beiträge: 357
|
Verfasst: Di 07.07.09 12:57
Cool, es funktioniert endlich.
Vielen Dank für die Hilfe.
MfG
|
|
Xentar
      
Beiträge: 2077
Erhaltene Danke: 2
Win XP
Delphi 5 Ent., Delphi 2007 Prof
|
Verfasst: Di 07.07.09 13:10
Ähh, nochwas..
du greifst aus einem Thread heraus direkt auf Komponenten des Formulars zu (Label, Gauge)
Das ist nicht gut. Such mal nach dem Stichwort Synchronisierung bzw. Synchronize.
_________________ PROGRAMMER: A device for converting coffee into software.
|
|
jackie05 
      
Beiträge: 357
|
Verfasst: Di 07.07.09 13:32
Danke, das ist mir auch schon aufgefallen, leider bin ich noch neu was Thread Programmierung angeht.
Ich habe da noch eine Frage:
Wie könnte ich mein Programmcode so abändern, das ich über IDs die Threads starten kann, um später die einzelnen Threads ansprechen zu kännen?
Ich habe mir das so vorgestellt:
Delphi-Quelltext 1:
| MyThread.Download(URL, Output, ID); |
und später möchte ich einen Download anhalten anhand der ID.
Wie könnte ich das ambesten realisieren?
MfG
--- Moderiert von Narses: Beiträge zusammengefasst---
Ich könnte ja einfach nen Array für den Thread erstellen:
Delphi-Quelltext 1:
| Thread: array of TMyThread; |
sollte ja eigentlich gehen und dann kann ich einen der array von TMyThread ändern.
Wenn ich falsch liege, dann entschuldigt mich.
Ich werde es einfach mal versuchen.
MfG
|
|
jackie05 
      
Beiträge: 357
|
Verfasst: Di 07.07.09 19:35
Hallo nochmal und sry wegen Doppelpost, aber des wird dann unübersichtlich wenn ich mein letzter Post editiere.
Zu meinem Problem:
Ich habe das mit array jetzt hinbekommen aber sobald mehr wie 1 Download in der Liste ist und ich einen Download starten möchte, dann bekomme ich folgende fehlermeldung:
Delphi-Quelltext 1:
| Project Project1.exe raised exception class EAccessViolation with message 'Access violation at adress 0047E61A in module 'Project1.exe'. Read of adress 0000005C'. Process stopped. Use Step or Run to continue. |
Wenn ich nur ein Download in der Liste eintrage und den Download starten möchte, dann funktioniert es.
Nur wenn ich mehr Downloads in der Liste eintrage, dann kommt dieser fehlermeldung.
Hier mal mein Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, StdCtrls, Gauges, ComCtrls, Menus;
type TForm1 = class(TForm) Label4: TLabel; liste: TListView; Button3: TButton; PopupMenu1: TPopupMenu; Dateidownloaden: TMenuItem; N4: TMenuItem; Eintragentfernen1: TMenuItem; AusgewhlteEintrgeentfernen1: TMenuItem; Edit1: TEdit; Edit2: TEdit; Label1: TLabel; procedure FormCreate(Sender: TObject); procedure Button3Click(Sender: TObject); procedure listeChange(Sender: TObject; Item: TListItem; Change: TItemChange); procedure DateidownloadenClick(Sender: TObject); private public end;
type TMyThread = class(TThread)
protected procedure Execute; override; private HTTP: TIdHTTP; fs: TFileStream; Dateiname: string; DownloadURL: string; MinKB,MaxKB: integer; ID: integer; Download: boolean; procedure HTTPWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer); procedure HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer); end;
var Form1: TForm1; Thread: array of TMyThread; ListCol: TListColumn; ListIt: TListItem; ThreadNr: integer;
implementation
{$R *.dfm}
procedure TMyThread.Execute; var i: integer; begin for i:=0 to Form1.liste.Items.Count-1 do begin if (Thread[i].Download = true) then begin Thread[i].HTTP := TIdHTTP.Create(nil); Thread[i].HTTP.Request.UserAgent := 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'; Thread[i].HTTP.HandleRedirects := true; Thread[i].HTTP.OnWork:=HTTPWork; Thread[i].HTTP.OnWorkBegin:=HTTPWorkBegin;
Thread[i].fs := TFileStream.Create(Thread[i].Dateiname,fmCreate); Thread[i].HTTP.Get(Thread[i].DownloadURL,Thread[i].fs);
Thread[i].HTTP.Free; Thread[i].fs.Free; end; end; end;
procedure TMyThread.HTTPWork(Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer); var i: integer; begin for i:=0 to Form1.liste.Items.Count-1 do begin if (Thread[i].Download = true) then begin Thread[i].MinKB := AWorkCount; Form1.liste.Items[ID].SubItems[2] := IntToStr(Thread[i].MinKB div 1024)+' / '+IntToStr(Thread[i].MaxKB div 1024)+' KB'; Application.ProcessMessages; end; end; end;
procedure TMyThread.HTTPWorkBegin(Sender: TObject; AWorkMode: TWorkMode; const AWorkCountMax: Integer); var i: integer; begin for i:=0 to Form1.liste.Items.Count-1 do begin if (Thread[i].Download = true) then begin Thread[i].MaxKB := AWorkCountMax; Form1.liste.Items[i].SubItems[2] := IntToStr(Thread[i].MaxKB div 1024)+' KB'; end; end; end;
procedure TForm1.FormCreate(Sender: TObject); begin ThreadNr := 0; end;
procedure TForm1.Button3Click(Sender: TObject); begin ThreadNr := ThreadNr + 1; SetLength(Thread,ThreadNr); ListIt := Liste.Items.Add(); ListIt.Caption := ''; ListIt.SubItems.Add('test'); ListIt.SubItems.Add('1700 KB'); ListIt.SubItems.Add('0 / 0');
ListIt.Data := StrNew(PChar(Edit1.Text)); end;
procedure TForm1.listeChange(Sender: TObject; Item: TListItem; Change: TItemChange); begin if Liste.Selected = nil then begin Dateidownloaden.Enabled := false; Eintragentfernen1.Enabled := false; AusgewhlteEintrgeentfernen1.Enabled := false; end else begin Dateidownloaden.Enabled := true; Eintragentfernen1.Enabled := true; AusgewhlteEintrgeentfernen1.Enabled := true; end; end;
procedure TForm1.DateidownloadenClick(Sender: TObject); begin if Liste.Selected = nil then begin
end else begin Thread[Liste.ItemIndex] := TMyThread.Create(false); Thread[Liste.ItemIndex].Dateiname := Edit2.Text; Thread[Liste.ItemIndex].DownloadURL := Edit1.Text; Thread[Liste.ItemIndex].Download := true; Label1.Caption := IntToStr(Liste.ItemIndex); end; end;
end. |
Delphi zeigt mir den fehler in Zeile 110 an.
Was habe ich falsch gemacht?
Danke schonmal.
MfG
|
|
Xentar
      
Beiträge: 2077
Erhaltene Danke: 2
Win XP
Delphi 5 Ent., Delphi 2007 Prof
|
Verfasst: Di 07.07.09 19:51
Weiß nicht, ob das für den Fehler verantwortlich ist, aber du greifst schon wieder von mehreren Threads aus, ohne zu synchronisieren, auf gemeinsame Objekte zu!
Lies dir nochmal die Tutorials dazu durch!
Und nebenbei: das = true in einer If-Abfrage kann / sollte man weg lassen. Und statt if blah = false schreibt man if not blah
_________________ PROGRAMMER: A device for converting coffee into software.
|
|
jackie05 
      
Beiträge: 357
|
Verfasst: Mi 08.07.09 12:05
Danke.
Ich habe mehrmals die Tutorials gelesen und bei Wiki ebenfalls.
Irgendwie weiss ich nicht wie ich es Synchronisieren kann, ich habe es damit versucht:
Delphi-Quelltext
Ich habe mal mein Projekt im anhang hochgeladen.
Ich wäre euch sehr dankbar, wenn da einer mal rein kucken könnte, was ich falsch gemacht habe.
Natürlich nur, wenn es nix ausmachen würde.
In der Liste kann man mit der rechten Maustaste einen Download starten, starte ich nun den 2ten Download, wenn der erste Download noch nicht beendet ist. dann stimmen die KB anzeige in der Liste nicht mehr, aber downloaden tut es trotzdem.
Was habe ich falsch gemacht?
Ich bedanke mich schonmal im Voraus.
MfG
Einloggen, um Attachments anzusehen!
|
|
jackie05 
      
Beiträge: 357
|
Verfasst: Sa 11.07.09 19:23
Hallo nochmal,
ich möchte mehrere Threads nacheinander starten, ohne erst zu warten, bis der erste Thread fertig abgearbeitet ist.
Hier mal der Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; ListBox1: TListBox; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private public end;
type TMyThread = class(TThread) protected procedure Execute; override; private ID: integer; Zaehler: integer; end;
var Form1: TForm1; ThreadNr: integer; Thread: array of TMyThread;
implementation
{$R *.dfm}
procedure TMyThread.Execute; var i,j: integer; begin for i:=0 to High(Thread) do begin for j:=0 to 10000 do begin Thread[i].Zaehler := j; end; end; end;
procedure TForm1.Button1Click(Sender: TObject); begin ThreadNr := ThreadNr + 1; SetLength(Thread,ThreadNr); Thread[ThreadNr-1] := TMyThread.Create(false); end;
procedure TForm1.FormCreate(Sender: TObject); begin ThreadNr := 0; end;
end. |
Wenn ich den ersten Thread starte und nach ne weile den 2ten Thread starte, dann hängt sich das Programm kurz auf und dann wird erst der erste Thread zuende abgearbeitet und dann fängt der 2te Thread an zu arbeiten.
Wie mache ich das ambesten, dass ich mehrere Threads zusammen ausführen lassen kann?
Danke schonmal.
MfG
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 11.07.09 20:39
Du greifst in der Threadmethode schreibend über eine globale Variable auf die einzelnen Threads zu?
Und da wunderst du dich, dass es Probleme gibt?!?
Wie es richtig ist: Thread erzeugen, mit dem Parameter True (suspended, also angehalten, erzeugen). Dann übergibst du die zur Berechnung notwendigen Parameter, denn auf die Außenwelt darf der Thread ja nur synchronisiert zugreifen. Danach, wenn der Thread die Parameter hat, lässt du ihn mit Resume laufen.
Wenn der Thread jetzt fertig ist und das Ergebnis mitteilen will, dann tut er das via Synchronize.
Am besten pack deinen Thread in eine eigene Unit und lass die globalen Variablen weg, dann kommst du gar nicht in die Versuchung da irgendetwas aus dem Thread heraus zu nutzen...
|
|
Andreas L.
      
Beiträge: 1703
Erhaltene Danke: 25
Windows Vista / Windows 10
Delphi 2009 Pro (JVCL, DragDrop, rmKlever, ICS, EmbeddedWB, DEC, Indy)
|
Verfasst: Sa 11.07.09 22:11
Nur so eine Idee:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88:
| type TDownload = class;
TDownloadList = class(TObjectList) protected procedure SetItem(Index: Integer; Value: TDownload); function GetItem(Index: Integer):TDownload; public function Add(AURL, AOuput: String):Integer; property Items[Index: Integer]:TDownload read GetItem write SetItem; default; end;
TDownload = class(TThread) private FURL: String; FOutput: String; protected procedure Execute; override; public property URL: String read FURL; property Output: String read FOutput; end;
...
procedure TDownloadList.SetItem(Index: Integer; value: TDownload); begin inherited Items[Index] := Value; end;
function TDownloadList.GetItem(Index: Integer):TDownload; begin Result := inherited Items[Index] as TDownload; end;
function TDownloadList.Add(AURL, AOutput: String):Integer; var NewDL: TDownload; begin NewDL := TDownload.Create(True); NewDL.FURL := AURL; NewDL.FOutput := AOutput; NewDL.Resume; Result := inherited Add(NewDL); end;
procedure TDownload.Execute; var http: TIdHttp; fs: TFileStream; begin http := TIdHttp.Create(nil); try fs := TFileStream.Create(Output, fmCreate); try http.Get(URL, fs); finally fs.Free; end; finally http.Free; end; end;
var MyDownloadList: TDownloadList;
MyDownloadList := TDownloadList.Create;
MyDownloadList.Free;
procedure TForm1.DownloadFiles; const RemoteDir = 'http://deinserver.de/files/'; LocalDir = 'C:\temp\'; var I: Integer; begin for i := 0 to FileList.Count -1 do MyDownloadList.Add(RemoteDir + FileList[i], LocalDir + FileList[i]); end; |
ungetestet
|
|
|