Autor Beitrag
jackie05
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: 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.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 566
Erhaltene Danke: 10

Win 7, Ubuntu
Lazarus, Turbo Delphi, Delphu 7 PE
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: 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:
ausblenden volle Höhe Delphi-Quelltext
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
    { Private declarations }
  public
    { Public declarations }
  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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 46
Erhaltene Danke: 1

Winxxx
TP, BP, Delphi 1 - 2009
BeitragVerfasst: Di 07.07.09 12:51 
Du musst die Eventhandler noch der HTTP - Komponente zuweisen:


ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: Di 07.07.09 12:57 
Cool, es funktioniert endlich. :wink:

Vielen Dank für die Hilfe.

MfG
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: 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:
ausblenden 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 user profile iconNarses: Beiträge zusammengefasst---

Ich könnte ja einfach nen Array für den Thread erstellen:
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: 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:
ausblenden 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:
ausblenden volle Höhe Delphi-Quelltext
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
    { Private declarations }
  public
    { Public declarations }
  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 gestartet und Datei downloaden
      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
  //Wenn ThreadID = Form1.liste.ItemIndex entspricht, dann dieser
  //Download ausführen
  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
  //Wenn ThreadID = Form1.liste.ItemIndex entspricht, dann dieser
  //Download ausführen
  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 anfangs auf 0 setzen, diese wird benötigt um
  //die array größe später zu ändern
  ThreadNr := 0;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  //array für den Thread immer um 1 vergrößern und anschließend
  //in die Liste eintragen
  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
  //Wenn kein Eintrag in der Liste ausgewählt ist,
  //dann alle Buttons deaktivieren
  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 starten und anschließend Datei Downloaden
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: 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:
ausblenden Delphi-Quelltext
1:
Synchronize({*****});					


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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 357



BeitragVerfasst: 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:
ausblenden volle Höhe Delphi-Quelltext
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
    { Private declarations }
  public
    { Public declarations }
  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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 11.07.09 20:39 
Du greifst in der Threadmethode schreibend über eine globale Variable auf die einzelnen Threads zu? :shock:
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.
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1703
Erhaltene Danke: 25

Windows Vista / Windows 10
Delphi 2009 Pro (JVCL, DragDrop, rmKlever, ICS, EmbeddedWB, DEC, Indy)
BeitragVerfasst: Sa 11.07.09 22:11 
Nur so eine Idee:

ausblenden volle Höhe Delphi-Quelltext
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;

...

{ TDownloadList }
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;

{ TDownload }
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;

//Anwendungsbeispiel:

var
  MyDownloadList: TDownloadList;

//FormOnCreate
  MyDownloadList := TDownloadList.Create;

//FormOnDestroy
  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 //FileList ist eine stringlist die Dateinamen enthält
    MyDownloadList.Add(RemoteDir + FileList[i], LocalDir + FileList[i]);
end;

ungetestet