Einzelnen Beitrag anzeigen

IMPEGA

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

AW: Threads und StringList

  Alt Heute, 12:06
Wenn du in einem Thread "UpdateStatus" aufrufst wird das hier im Hauptthread (durch das Synchronize) aufgerufen.

Delphi-Quellcode:
      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);
Dabei greifst du auf die (globale) Variable "Ergebnisliste" zu. Wenn ein weiterer Thread aber gerade dabei ist in diese Ergebnisliste zu schreiben, kommt es zu Problemen.

Lösung: Auch dort die CriticalSection benutzen.

Des weiteren nutzt du 1 CriticalSection für 2 unterschiedliche Listen. Das funktioniert zwar, aber du sperrst immer 2 Listen, was das ganze verlangsamen kann.

Lösung: 2 CriticalSections oder TMonior.Enter(Liste) verwenden.
Das mit CriticalSection gedoppelt, das vielleicht später, nun habe ich versucht die StringList zu individualisieren. Wie gesagt, ich gehe nur Step by Step.
Nur als grobe Info. Kann ich einfach eine Zweite CriticalSection einbauen und gut ist? Die eine bleibt für FUrlList und eine zweite für FResultList ???
@himitsu Sorry, auf deinen Vorschlag gehe ich erst nicht ein, bin eh schon am Limit. Für heute reicht es mir, glaube ich.

Hier noch meine Änderung, jetzt sollte je Thread eine Liste benutzt werden.
Delphi-Quellcode:
    procedure ButtonStartClick(Sender: TObject);
    procedure ButtonAbbruchClick(Sender: TObject);
    procedure ButtonWeiterClick(Sender: TObject);
    procedure ButtonEXITClick(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;
    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;
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
  FResultList : TstringList;
  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;

          Sleep(Random(2000)); {Hier kommt später meine Aufgabe}

          FResultList := TStringList.Create; //--> Hier geändert Pro Job wird eine Liste erstellt und zerstört
          try
            try
  // 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(FResultList);
            except
              on E: Exception do
                begin
                  FResultList.Add('FAIL: ' + Url + ' - ' + E.Message);
                  UpdateStatus(FResultList);
                end;
            end;
          finally
            FreeAndNil(FResultList); //--> Hier geändert Pro Job wird eine Liste erstellt und zerstört
          end;

          TThread.Synchronize(nil, procedure
            begin
              Dec(Form1.IdleJobs);
              Form1.UpdateLabelJobs;

              Inc(Form1.JobsFinished);
              Form1.UpdateLabelFinished;
            end);
        end;
    finally
      FreeAndNil(FResultList);
      FreeAndNil(Http);

      TThread.Synchronize(nil, procedure
        begin
          Dec(Form1.ActiveThreads);
          Form1.UpdateLabelActiveThreads;
        end);
    end;
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
    begin
      Threads[Index] := TURLCheckerThread.Create(UrlList, CriticalSection,
        procedure(Msg: TstringList)
          var
            ErgebnisListe : TStringList;
          begin
            RichEditLog.Lines.Add(Msg.Text); {Ergebnis als StringList}
          end, @ShouldStop);

     {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: TstringList)
          begin
            RichEditLog.Lines.Add(Msg.Text); {Ergebnis als StringList}
          end, @ShouldStop);

     {Zähle aktive Threads}
      Inc(ActiveThreads);
      LabelActiveThreads.Caption := IntToStr(ActiveThreads);
    end;
end;

procedure TForm1.ButtonAbbruchClick(Sender: TObject);
begin
  ShouldStop := True;
end;

procedure TForm1.ButtonEXITClick(Sender: TObject);
begin
  ShouldStop := True;
  Close;
end;


procedure TForm1.UpdateLabelActiveThreads;
begin
  LabelActiveThreads.Caption := IntToStr(ActiveThreads);
end;

procedure TForm1.UpdateLabelJobs;
begin
  LabelIdle.Caption := IntToStr(IdleJobs);
end;

procedure TForm1.UpdateLabelFinished;
begin
  LabelFinished.Caption := IntToStr(JobsFinished);
end;
EDIT, erst jetzt gesehen, da stimmt noch etwas nicht. Es reicht für heute. Sonst kommt nur noch Misst dabei raus.


Das Teil mag ich auch nicht, erstmal habe ich auch kein Plan wie ich es korrigieren/verbessern kann.
Delphi-Quellcode:
      TThread.Synchronize(nil, procedure
        begin
          Dec(Form1.ActiveThreads);
          Form1.UpdateLabelActiveThreads;
        end);

Geändert von IMPEGA (Heute um 12:40 Uhr)
  Mit Zitat antworten Zitat