AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Threads und StringList

Ein Thema von IMPEGA · begonnen am 24. Mai 2025 · letzter Beitrag vom 25. Mai 2025
 
IMPEGA

Registriert seit: 19. Jan 2008
Ort: Brhv
97 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

Threads und StringList

  Alt 24. Mai 2025, 23:56
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.
  Mit Zitat antworten Zitat
 


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:46 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz