|
![]() |
|
Registriert seit: 19. Jan 2008 Ort: Brhv 97 Beiträge Delphi 10.2 Tokyo Professional |
#1
Delphi-Version: 5
Ich versuche mich erneut etwas mit Threads zu beschäftigen.
Das Thema ist schon ein wenig kompliziert für mich. Ich habe inzwischen schon etwas zusammengebaut, habe aber dazu ein Paar bzw. eine wichtige Frage. Welche Methode ist Threadsicher, oder eben besser. Vorausgesetzt, so kann ich es überhaupt aufbauen. Von dem ganzen Lesen bin ich schon total durcheinander. Hier mein 2 Methoden, ich denke beide kann ich verwenden, leider kann ich aber nicht erkennen, welche davon besser ist. Sorry für die möglichen Fehler, ich bin nur ein Hobby-Programmierer. Hintergrund ist halt, eine sehr große Liste aus Datei einlesen, in mehreren Threads Daten abfragen. Nach sagen wir 30 Minuten, anhalten. Danach einfach weiter die Liste abarbeiten. Bei Destroy kommt später noch Speichern der Datei. Bei erneutem Programmstart, wird die kleinere Datei genommen und weiter damit gearbeitet. So in etwa soll es funktionieren. Ein ThreadPool in der Form kriege ich nicht hin, also versuche ich es so. Es handelt sich um eine sehr große Datei, deshalb muss es in mehreren Etappen erledigt werden. Meine Frage bezieht sich hauptsächlich auf die StringList im Thread und als Ergebnis. 1: Mein Favorit, weil ich sowohl String als auch StringList nutzen kann.
Delphi-Quellcode:
procedure ButtonAbbruchClick(Sender: TObject);
procedure ButtonEXITClick(Sender: TObject); procedure ButtonStartClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ButtonWeiterClick(Sender: TObject); private { Private-Deklarationen } CriticalSection : TCriticalSection; UrlList : TStringList; ErgebnisListe : TStringList; ActiveThreads : Int64; IdleJobs : Int64; JobsFinished : Int64; procedure UpdateLabelActiveThreads; procedure UpdateLabelJobs; procedure UpdateLabelFinished; public { Public-Deklarationen } end; var Form1: TForm1; ShouldStop: Boolean = False; implementation {$R *.dfm} type TURLCheckerThread = class(TThread) private FUrlList : TStrings; FResultList : TStrings; FCriticalSection : TCriticalSection; FOnStatus : TProc<string>; FShouldStop : PBoolean; protected procedure Execute; override; procedure UpdateStatus(const Msg: string); public constructor Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<string>; ShouldStop: PBoolean; ResultList: TStrings); end; { TWorkerThread } constructor TURLCheckerThread.Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<string>; ShouldStop: PBoolean; ResultList: TStrings); begin inherited Create(False); FUrlList := UrlList; FResultList := ResultList; FCriticalSection := CriticalSection; FOnStatus := OnStatus; FShouldStop := ShouldStop; FreeOnTerminate := True; {Zähle aktive Threads} TThread.Synchronize(nil, procedure begin Inc(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end); end; procedure TURLCheckerThread.UpdateStatus(const Msg: string); begin if Assigned(FOnStatus) then TThread.Synchronize(TThread.Current, procedure begin FOnStatus(Msg); end); end; procedure TURLCheckerThread.Execute; var Url : string; Http : TIdHTTP; begin Http := TIdHTTP.Create(nil); try while not Terminated and not FShouldStop^ do begin Url := ''; FCriticalSection.Acquire; try if FUrlList.Count > 0 then begin Url := FUrlList[0]; FUrlList.Delete(0); end; finally FCriticalSection.Release; end; if Url = '' then Break; try Sleep(Random(2000)); {Hier kommt später meine Aufgabe} // Http.Head(Url); FCriticalSection.Acquire; {Keine Ahnung ob es richtig ist, habe aber rausgelesen dass man StringList auf jeden Fall mit CriticalSection absichern soll} try FResultList.Add('OK: ' + Url); {Ergebnis in StringList eintragen} FResultList.Add('Zweite Zeile'); {Ergebnis in StringList eintragen} FResultList.Add('Dritte Zeile'); {Ergebnis in StringList eintragen} finally FCriticalSection.Release; end; UpdateStatus('OK: ' + Url); {Damit kann ich zB. die Status ON oder OFF ausgeben, oder weglassen und NUR FResult als Ergebnis nutzen} except on E: Exception do UpdateStatus('FAIL: ' + Url + ' - ' + E.Message); end; TThread.Synchronize(nil, procedure begin Dec(Form1.IdleJobs); Form1.UpdateLabelJobs; Inc(Form1.JobsFinished); Form1.UpdateLabelFinished; end); end; finally Http.Free; TThread.Synchronize(nil, procedure begin Dec(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end); end; end; procedure TForm1.FormDestroy(Sender: TObject); begin FreeAndNil(CriticalSection); FreeAndNil(ErgebnisListe); FreeAndNil(UrlList); end; procedure TForm1.ButtonStartClick(Sender: TObject); var Threads : array[0..99] of TURLCheckerThread; Index : Integer; begin if Assigned(CriticalSection) then FreeAndNil(CriticalSection); {Falls ich nach Cancel noch mal Start drücke statt Weiter} if Assigned(UrlList) then FreeAndNil(UrlList); {Falls ich nach Cancel noch mal Start drücke statt Weiter} UrlList := TStringList.Create; CriticalSection := TCriticalSection.Create; ShouldStop := False; for Index := 1 to 100 do begin URLList.Add('http://google.com/' + IntToStr(Index)); end; LabelJobs.Caption := '100'; LabelIdle.Caption := '100'; IdleJobs := 100; JobsFinished := 0; ErgebnisListe := TStringList.Create; for Index := 1 to SpinEditThreads.Value do Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection, procedure(Msg: string) begin RichEditLog.Lines.Add(Msg); RichEditLog.Lines.Add(ErgebnisListe.Text); ErgebnisListe.Clear; {Nicht vergessen, sonst wird die StringList immer weiter befüllt} end, @ShouldStop, ErgebnisListe); end; procedure TForm1.ButtonWeiterClick(Sender: TObject); var Threads : array[0..99] of TURLCheckerThread; Index : Integer; begin ShouldStop := False; for Index := 1 to SpinEditThreads.Value do Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection, procedure(Msg: string) begin RichEditLog.Lines.Add(Msg); {Ergebnis als String} RichEditLog.Lines.Add(ErgebnisListe.Text); {Ergebnis als StringList} ErgebnisListe.Clear; {Sehr wichtig, StringList resetten} end, @ShouldStop, ErgebnisListe); end; procedure TForm1.ButtonAbbruchClick(Sender: TObject); begin ShouldStop := True; end; procedure TForm1.UpdateLabelActiveThreads; begin TThread.Synchronize(nil, procedure begin LabelActiveThreads.Caption := IntToStr(ActiveThreads); end); end; procedure TForm1.UpdateLabelJobs; begin TThread.Synchronize(nil, procedure begin LabelIdle.Caption := IntToStr(IdleJobs); end); end; procedure TForm1.UpdateLabelFinished; begin TThread.Synchronize(nil, procedure begin LabelFinished.Caption := IntToStr(JobsFinished); end); end; procedure TForm1.ButtonEXITClick(Sender: TObject); begin ShouldStop := True; Close; end; 2:
Delphi-Quellcode:
procedure ButtonAbbruchClick(Sender: TObject);
procedure ButtonEXITClick(Sender: TObject); procedure ButtonStartClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ButtonWeiterClick(Sender: TObject); private { Private-Deklarationen } CriticalSection : TCriticalSection; UrlList : TStringList; ActiveThreads : Int64; IdleJobs : Int64; JobsFinished : Int64; procedure UpdateLabelActiveThreads; procedure UpdateLabelJobs; procedure UpdateLabelFinished; public { Public-Deklarationen } end; var Form1 : TForm1; ShouldStop : Boolean = False; implementation {$R *.dfm} type TURLCheckerThread = class(TThread) private FUrlList : TStrings; FCriticalSection : TCriticalSection; FOnStatus : TProc<TStringList>; FShouldStop : PBoolean; protected procedure Execute; override; procedure UpdateStatus(const Msg: TStringList); public constructor Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<TStringList>; ShouldStop: PBoolean); end; { TWorkerThread } constructor TURLCheckerThread.Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<TStringList>; ShouldStop: PBoolean); begin inherited Create(False); FUrlList := UrlList; FCriticalSection := CriticalSection; FOnStatus := OnStatus; FShouldStop := ShouldStop; FreeOnTerminate := True; {Zähle aktive Threads} TThread.Synchronize(nil, procedure begin Inc(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end); end; procedure TURLCheckerThread.UpdateStatus(const Msg: TStringList); begin if Assigned(FOnStatus) then TThread.Synchronize(TThread.Current, procedure begin FOnStatus(Msg); end); end; procedure TURLCheckerThread.Execute; var FUrl : string; FHttp : TIdHTTP; FResult : TStringList; begin FHttp := TIdHTTP.Create(nil); FResult := TStringList.Create; try while not Terminated and not FShouldStop^ do begin FUrl := ''; FCriticalSection.Acquire; try if FUrlList.Count > 0 then begin FUrl := FUrlList[0]; FUrlList.Delete(0); end; finally FCriticalSection.Release; end; if FUrl = '' then Break; try Sleep(Random(2000)); {Hier kommt später meine Aufgabe} // Http.Head(Url); FCriticalSection.Acquire; {Keine Ahnung ob es richtig ist, habe aber rausgelesen dass man StringList auf jeden Fall mit CriticalSection absichern soll} try FResult.Add('OK: ' + FUrl); FResult.Add('Zweite Zeile'); finally FCriticalSection.Release; end; except on E: Exception do begin FResult.Add('FAIL: ' + FUrl + ' - ' + E.Message); end; end; UpdateStatus(FResult); TThread.Synchronize(nil, procedure begin Dec(Form1.IdleJobs); Form1.UpdateLabelJobs; Inc(Form1.JobsFinished); Form1.UpdateLabelFinished; end); end; finally FreeAndNil(FHttp); FreeAndNil(FResult); TThread.Synchronize(nil, procedure begin Dec(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end); end; end; procedure TForm1.FormDestroy(Sender: TObject); begin FreeAndNil(CriticalSection); FreeAndNil(UrlList); end; procedure TForm1.ButtonStartClick(Sender: TObject); var Threads : array[0..99] of TURLCheckerThread; Index : Integer; begin if Assigned(CriticalSection) then FreeAndNil(CriticalSection); {Falls ich nach Cancel noch mal Start drücke statt Weiter} if Assigned(UrlList) then FreeAndNil(UrlList); {Falls ich nach Cancel noch mal Start drücke statt Weiter} UrlList := TStringList.Create; CriticalSection := TCriticalSection.Create; ShouldStop := False; for Index := 1 to 100 do begin URLList.Add('http://google.com/' + IntToStr(Index)); end; LabelJobs.Caption := '100'; LabelIdle.Caption := '100'; IdleJobs := 100; JobsFinished := 0; for Index := 1 to SpinEditThreads.Value do Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection, procedure(Msg: TStringList) begin RichEditLog.Lines.AddStrings(Msg); {Statusanzeige, z.B. in Memo oder Listbox Msg = das Ergebnis vom Thread} end, @ShouldStop); end; procedure TForm1.ButtonWeiterClick(Sender: TObject); var Threads : array[0..99] of TURLCheckerThread; Index : Integer; begin ShouldStop := False; for Index := 1 to SpinEditThreads.Value do Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection, procedure(Msg: TStringList) begin RichEditLog.Lines.AddStrings(Msg); {Statusanzeige, z.B. in Memo oder Listbox Msg = das Ergebnis vom Thread} end, @ShouldStop); end; procedure TForm1.ButtonAbbruchClick(Sender: TObject); begin ShouldStop := True; end; procedure TForm1.UpdateLabelActiveThreads; begin TThread.Synchronize(nil, procedure begin LabelActiveThreads.Caption := IntToStr(ActiveThreads); end); end; procedure TForm1.UpdateLabelJobs; begin TThread.Synchronize(nil, procedure begin LabelIdle.Caption := IntToStr(IdleJobs); end); end; procedure TForm1.UpdateLabelFinished; begin TThread.Synchronize(nil, procedure begin LabelFinished.Caption := IntToStr(JobsFinished); end); end; procedure TForm1.ButtonEXITClick(Sender: TObject); begin ShouldStop := True; Close; end; Allgemeine Fehler im Code bitte auch aufzeigen, allerdings unbedingt mit Erklärung für Non-Profi. Momwentan bin ich stark überladen mit dem Zeug. |
![]() |
Online
Registriert seit: 10. Jun 2003 Ort: Berlin 9.982 Beiträge Delphi 12 Athens |
#2
Zunächst einmal zu ein paar Fehlern:
Delphi-Quellcode:
Erstens verwendest du TThread.Synchronize in TURLCheckerThread.Create. TURLCheckerThread.Create rufst du aber im Hauptthread auf. Deshalb brauchst du hier keine Synchronisation. In aktuellen Delphiversionen wird das korrekt behandelt, weshalb es hier keinen Fehler gibt.
constructor TURLCheckerThread.Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<string>; ShouldStop: PBoolean; ResultList: TStrings);
begin inherited Create(False); FUrlList := UrlList; FResultList := ResultList; FCriticalSection := CriticalSection; FOnStatus := OnStatus; FShouldStop := ShouldStop; FreeOnTerminate := True; {Zähle aktive Threads} TThread.Synchronize(nil, procedure begin Inc(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end); end; procedure TURLCheckerThread.UpdateStatus(const Msg: string); begin if Assigned(FOnStatus) then TThread.Synchronize(TThread.Current, procedure begin FOnStatus(Msg); end); end; Zweitens rufst du in UpdateStatus die Synchronisation mit TThread.Synchronize(TThread.Current... auf. Damit kannst du dir die Synchronisation auch sparen, denn wenn du mit dem aktuellen Thread synchronisierst, bleibst du im gleichen Threrad. Wenn du nil verwendest, wird der Code im Hauptthread ausgeführt. Drittens packst du die Threads nach der Erstellung in ein Array. Du verwendest aber FreeOnTerminate, sprich der Thread wird nach Beendigung seiner Aufgabe automatisch freigegeben. Du weißt aber nicht, wann das passiert. Deshalb darfst du einen solchen Thread nie in eine Variable speichern, damit du nicht aus Versehen darauf zugreifst. Du weißt ja dann gar nicht, ob der Thread noch existiert, so dass das Fehler geben kann. Viertens ist zwar kein Fehler, aber sehr ungünstig und fast so schlecht wie ein Fehler: Du greifst aus deiner Threadklasse mit Form1.UpdateLabelActiveThreads auf Form1 zu. Du solltest stattdessen die Threadklasse besser in eine eigene Unit packen und alles, was diese braucht, dorthin übergeben. Dann schaffst du dir keine unnötigen Abhängigkeiten und hast eine bessere Trennung des Ablaufs im Thread. Nun zum Thema: Bei Threads muss man sich immer anschauen, was wie viel Zeit benötigt. Wenn man z.B. Threads hat, die einzeln nicht so lange brauchen, muss man aufpassen, dass man für deren Verwaltung nicht zu viel Aufwand betreibt. Denn ansonsten wird die Aufgabe insgesamt mit Threads nicht schneller erledigt als ohne. In deinem Fall brauchst du viel Zeit, um die URLs zu prüfen. Daher ist der Aufwand für die Listen im Vergleich viel geringer. Deshalb ist es gar kein Problem, wenn jeder Thread eine eigene Ergebnisliste bekommt, die du dann im OnStatus aus dem Thread übergeben bekommst. Und die URL kannst du ja einfach direkt als String übergeben. Denn in der Schleife bei der Erstellung der Threads läuft ja nichts parallel, so dass du einfach direkt auf die URL-Liste zugreifen kannst. Sprich als Pseudocode:
Delphi-Quellcode:
So erstellst du aber keine neuen Threads, wenn die ersten URLs abgearbeitet sind. Deshalb wären ein Threadpool oder auch TParallel.For Alternativen.
for Index := 0 to SpinEditThreads.Value - 1 do
begin TURLCheckerThread.Create(UrlList[i], ...); Inc(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end; Du kannst aber auch einfach die Liste auf die Anzahl der Threads verteilen und jedem Thread nicht eine URL geben, sondern eine Liste, die dieser dann abarbeitet. Sprich wenn du 300 URLs hast und 5 Threads möchtest, gibst du jedem Thread eine Liste von 60 URLs. Das Thema Multithreading ist tatsächlich sehr umfangreich. Das ist nur ein Anfang. Dazu ließe sich noch viel mehr schreiben, aber besser ist glaube ich, wenn du dir das erst einmal anschaust und ggf. Rückfragen stellst. Geändert von jaenicke (Heute um 12:44 Uhr) Grund: Synchronize falsch verstanden |
![]() |
Registriert seit: 19. Jan 2008 Ort: Brhv 97 Beiträge Delphi 10.2 Tokyo Professional |
#3
OK, das sind viele Fehler. Ich kann nur nach und nach versuchen, es zu verbessern.
Einige Sachen waren mir recht klar, dass es nicht gut ist, dass es aber so viel ist, hätte ich nicht gedacht. Ich werde versuchen etwas zu korrigieren und frage erneut. Allerdings Step by Step. Nur, damit ich mir etwas Arbeit sparen kann, und nicht erst die falsche Methode zu reparieren versuche. Welche Methode sollte ich mir vornehmen. Welche hat eben mehr Potenzial? Ich denke, herausgelesen zu haben, die erste wäre sicherer. Meine Kenntnisse, wie man sehen kann, reichen eher nicht aus, um es selbst zu bestimmen. |
![]() |
Online
Registriert seit: 25. Apr 2008 Ort: Neunkirchen 789 Beiträge |
#4
Zweitens rufst du in UpdateStatus die Synchronisation mit TThread.Synchronize(TThread.Current...
auf. Damit kannst du dir die Synchronisation auch sparen, denn wenn du mit dem aktuellen Thread synchronisierst, bleibst du im gleichen Threrad. Wenn du nil verwendest, wird der Code im Hauptthread ausgeführt.
![]() Synchronize bewirkt, dass der von AMethod festgelegte Aufruf im Haupt-Thread ausgeführt wird. Dabei werden Multithread-Konflikte verhindert. Der Parameter AThread wird dem Thread des Aufrufers zugeordnet.
Für statische Methoden können Sie AMethod jedem Thread mithilfe des Parameters AThread zuordnen. Außerdem können Sie nil/NULL als Parameter AThread verwenden, wenn Sie die Informationen für den Thread des Aufrufers im Haupt-Thread nicht brauchen. |
![]() |
Online
Registriert seit: 25. Apr 2008 Ort: Neunkirchen 789 Beiträge |
#5
Ich denke auch, dass es passieren kann, dass das Update im Hauptthread ausgeführt werden kann und auf die Ergebnisliste zugreift, während gleichzeitig ein anderer Thread auf die Ergebnisliste zugreift (Beispiel 1).
|
![]() |
Online
Registriert seit: 25. Apr 2008 Ort: Neunkirchen 789 Beiträge |
#6
Ich denke auch, dass es passieren kann, dass das Update im Hauptthread ausgeführt werden kann und auf die Ergebnisliste zugreift, während gleichzeitig ein anderer Thread auf die Ergebnisliste zugreift (Beispiel 1).
|
![]() |
Registriert seit: 19. Jan 2008 Ort: Brhv 97 Beiträge Delphi 10.2 Tokyo Professional |
#7
Ich denke auch, dass es passieren kann, dass das Update im Hauptthread ausgeführt werden kann und auf die Ergebnisliste zugreift, während gleichzeitig ein anderer Thread auf die Ergebnisliste zugreift (Beispiel 1).
Wenn du einen Fehler siehst, versuche doch mir zu helfen, statt nur zu sagen, dass es falsch ist. Gib mir wenigstens einen Ansatz, wonach ich suchen soll, schreibe kleines Beispiel oder wie auch immer. Nur zu sagen, was ich nicht beachtet habe, brauchst du nicht, es vermüllt nur den Thread. Bitte, sachlich oder gar nicht antworten. Es ist schwer genug für mich, das Ganze zu verstehen. jaenicke hat schon einiges klar definiert, sachliche Vorschläge gemacht, so dass ich es wenigstens etwas nachvollziehen konnte. Nun bin ich am Versuchen es zu korrigieren, allerdings was nicht im Kopf steck, springt dort nicht rein, nur weil du sagst, ich habe es nicht behandelt. Ja, ich habe es nicht behandelt, weil ich noch keine Ahnung habe, wie ich es anfassen soll. Mit deinen Antworten komme ich echt schlecht zu Recht, es demotiviert mich weiter zu fragen, also bitte lasse es sein. Danke. |
![]() |
Registriert seit: 19. Jan 2008 Ort: Brhv 97 Beiträge Delphi 10.2 Tokyo Professional |
#8
Erstmal vielen Dank für die Antworten.
Ich bleibe nun bei meinem ersten Beispiel, und gehe damit weiter. OK, wie gesagt, ich versuche Step by Step mein Code etwas zu verbessern. Erstmal die einfachen Sachen, die mir klar sind und ich verstanden habe, warum es anders muss. Korrigierter Code kommt gleich. Um es übersichtlich zu halten, stelle ich erst mal weitere Fragen. ![]() Drittens packst du die Threads nach der Erstellung in ein Array. Du verwendest aber FreeOnTerminate, sprich der Thread wird nach Beendigung seiner Aufgabe automatisch freigegeben. Du weißt aber nicht, wann das passiert. Deshalb darfst du einen solchen Thread nie in eine Variable speichern, damit du nicht aus Versehen darauf zugreifst. Du weißt ja dann gar nicht, ob der Thread noch existiert, so dass das Fehler geben kann.
Ich erkenne es auch nicht, wieso du vom Array sprichst. Weil, sehe oben : Ich dachte, es betrifft immer nur ein Thread. Bitte erneut um Aufklärung für Non-Pfofi. Mir ist klar, dass ich die StringList mit allen aktiven Threads füttern kann, (das will ich aber nicht) allerdings erkenne ich es nicht wie da ein Array gebildet wird und wie ich dementsprechend die Threads anders terminieren soll. ![]() Viertens ist zwar kein Fehler, aber sehr ungünstig und fast so schlecht wie ein Fehler:
Du greifst aus deiner Threadklasse mit Form1.UpdateLabelActiveThreads auf Form1 zu. Du solltest stattdessen die Threadklasse besser in eine eigene Unit packen und alles, was diese braucht, dorthin übergeben. Dann schaffst du dir keine unnötigen Abhängigkeiten und hast eine bessere Trennung des Ablaufs im Thread. Das Thema ist recht schwer, ich versuche aber so gut es geht es etwas zu verstehen, es ist aber echt nicht wirklich leicht. ![]() Threadpool oder auch TParallel.For Alternativen.
![]() Du kannst aber auch einfach die Liste auf die Anzahl der Threads verteilen und jedem Thread nicht eine URL geben, sondern eine Liste, die dieser dann abarbeitet. Sprich wenn du 300 URLs hast und 5 Threads möchtest, gibst du jedem Thread eine Liste von 60 URLs.
Delphi-Quellcode:
procedure ButtonAbbruchClick(Sender: TObject);
procedure ButtonEXITClick(Sender: TObject); procedure ButtonStartClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure ButtonWeiterClick(Sender: TObject); private { Private-Deklarationen } CriticalSection : TCriticalSection; UrlList : TStringList; ErgebnisListe : TStringList; ActiveThreads : Int64; IdleJobs : Int64; JobsFinished : Int64; procedure UpdateLabelActiveThreads; procedure UpdateLabelJobs; procedure UpdateLabelFinished; public { Public-Deklarationen } end; var Form1: TForm1; ShouldStop: Boolean = False; implementation {$R *.dfm} type TURLCheckerThread = class(TThread) private FUrlList : TStrings; FResultList : TStrings; FCriticalSection : TCriticalSection; FOnStatus : TProc<string>; FShouldStop : PBoolean; protected procedure Execute; override; procedure UpdateStatus(const Msg: string); public constructor Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<string>; ShouldStop: PBoolean; ResultList: TStrings); end; { TWorkerThread } constructor TURLCheckerThread.Create(UrlList: TStrings; CriticalSection: TCriticalSection; OnStatus: TProc<string>; ShouldStop: PBoolean; ResultList: TStrings); begin inherited Create(False); FUrlList := UrlList; FResultList := ResultList; FCriticalSection := CriticalSection; FOnStatus := OnStatus; FShouldStop := ShouldStop; FreeOnTerminate := True; end; procedure TURLCheckerThread.UpdateStatus(const Msg: string); begin if Assigned(FOnStatus) then TThread.Synchronize(nil, procedure begin FOnStatus(Msg); end); end; procedure TURLCheckerThread.Execute; var Url : string; Http : TIdHTTP; begin Http := TIdHTTP.Create(nil); try while not Terminated and not FShouldStop^ do begin Url := ''; FCriticalSection.Acquire; try if FUrlList.Count > 0 then begin Url := FUrlList[0]; FUrlList.Delete(0); end; finally FCriticalSection.Release; end; if Url = '' then Break; try Sleep(Random(2000)); {Hier kommt später meine Aufgabe} // Http.Head(Url); FCriticalSection.Acquire; {Keine Ahnung ob es richtig ist, habe aber rausgelesen dass man StringList auf jeden Fall mit CriticalSection absichern soll} try FResultList.Add('OK: ' + Url); {Ergebnis in StringList eintragen} FResultList.Add('Zweite Zeile'); {Ergebnis in StringList eintragen} FResultList.Add('Dritte Zeile'); {Ergebnis in StringList eintragen} finally FCriticalSection.Release; end; // UpdateStatus('OK: ' + Url); {Damit kann ich zB. die Status ON oder OFF ausgeben, oder weglassen und NUR FResult als Ergebnis nutzen} except on E: Exception do UpdateStatus('FAIL: ' + Url + ' - ' + E.Message); end; TThread.Synchronize(nil, procedure begin Dec(Form1.IdleJobs); Form1.UpdateLabelJobs; Inc(Form1.JobsFinished); Form1.UpdateLabelFinished; end); end; finally Http.Free; TThread.Synchronize(nil, procedure begin Dec(Form1.ActiveThreads); Form1.UpdateLabelActiveThreads; end); end; end; procedure TForm1.FormDestroy(Sender: TObject); begin FreeAndNil(CriticalSection); FreeAndNil(ErgebnisListe); FreeAndNil(UrlList); end; procedure TForm1.ButtonStartClick(Sender: TObject); var Threads : array[0..99] of TURLCheckerThread; Index : Integer; begin if Assigned(CriticalSection) then FreeAndNil(CriticalSection); {Falls ich nach Cancel noch mal Start drücke statt Weiter} if Assigned(UrlList) then FreeAndNil(UrlList); {Falls ich nach Cancel noch mal Start drücke statt Weiter} UrlList := TStringList.Create; CriticalSection := TCriticalSection.Create; ShouldStop := False; for Index := 1 to 100 do begin URLList.Add('http://google.com/' + IntToStr(Index)); end; LabelJobs.Caption := '100'; LabelIdle.Caption := '100'; IdleJobs := 100; JobsFinished := 0; ErgebnisListe := TStringList.Create; for Index := 1 to SpinEditThreads.Value do begin Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection, procedure(Msg: string) begin RichEditLog.Lines.Add(Msg); {Ergebnis als String} RichEditLog.Lines.Add(ErgebnisListe.Text); {Ergebnis als StringList} ErgebnisListe.Clear; {Nicht vergessen, sonst wird die StringList immer weiter befüllt} end, @ShouldStop, ErgebnisListe); {Zähle aktive Threads} Inc(ActiveThreads); LabelActiveThreads.Caption := IntToStr(ActiveThreads); end; end; procedure TForm1.ButtonWeiterClick(Sender: TObject); var Threads : array[0..99] of TURLCheckerThread; Index : Integer; begin ShouldStop := False; for Index := 1 to SpinEditThreads.Value do begin Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection, procedure(Msg: string) begin RichEditLog.Lines.Add(Msg); {Ergebnis als String} RichEditLog.Lines.Add(ErgebnisListe.Text); {Ergebnis als StringList} ErgebnisListe.Clear; {Nicht vergessen, sonst wird die StringList immer weiter befüllt} end, @ShouldStop, ErgebnisListe); {Zähle aktive Threads} Inc(ActiveThreads); LabelActiveThreads.Caption := IntToStr(ActiveThreads); end; end; procedure TForm1.ButtonAbbruchClick(Sender: TObject); begin ShouldStop := True; end; procedure TForm1.UpdateLabelActiveThreads; begin LabelActiveThreads.Caption := IntToStr(ActiveThreads); {Das war eigentlich nur ein Versehen mit Synchronize. Es war schon spät und viel Stoff für einen Abend :)} end; procedure TForm1.UpdateLabelJobs; begin LabelIdle.Caption := IntToStr(IdleJobs); end; procedure TForm1.UpdateLabelFinished; begin LabelFinished.Caption := IntToStr(JobsFinished); end; procedure TForm1.ButtonEXITClick(Sender: TObject); begin ShouldStop := True; Close; end; |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |