ColdZer0
ColdZer0

Reputation: 233

How to use for loop inside a TTask threadin delphi 10

How to use for loop inside a TTask properly? i keep getting only the last item of ListBox1 in the memo, for example if i have 5 items in ListBox1, i get the last item of ListBox1 5 times in memo1 !, what is wrong with code ?

var
  i: Integer;
  lPath: string;
begin
  for i := 0 to ListBox1.Items.Count - 1 do
  begin
    lPath := ListBox1.Items.Strings[i];
    TTask.Create(
      procedure
      var
        lHTTP: TIdHTTP;
        IdSSL: TIdSSLIOHandlerSocketOpenSSL;
      begin
        lHTTP := TIdHTTP.Create(nil);

        TThread.Synchronize(nil,
          procedure
          begin
            Form1.Caption := 'Task Running...';
          end
        );

        try
          lHTTP.ReadTimeout := 30000;
          lHTTP.HandleRedirects := True;
          IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
          IdSSL.SSLOptions.Method := sslvTLSv1;
          IdSSL.SSLOptions.Mode := sslmClient;
          lHTTP.IOHandler := IdSSL;
        Finally
          try
            lHTTP.Get('http://website.com/'+lPath, TStream(nil));
          Finally
            lHTTP.Free;
          end;
        end;

        TThread.Synchronize(nil,
          procedure
          begin
            Memo1.Lines.Add(lPath);
           end
        );

      end
    ).Start;

  end;
end;

Upvotes: 4

Views: 986

Answers (1)

NineBerry
NineBerry

Reputation: 28549

Here is the adapted code:

// Current method:
procedure TMyForm.XYZ
var
  i: Integer;
  lPath: string;
begin
  for i := 0 to ListBox1.Items.Count - 1 do
  begin
    lPath := ListBox1.Items.Strings[i];
    StartDownloadTask(lPath);
  end;
end;

// Put task creation in separate method:
procedure TMyForm.StartDownloadTask(lPath: string)
begin
    TTask.Create(
      procedure
      var
        lHTTP: TIdHTTP;
        IdSSL: TIdSSLIOHandlerSocketOpenSSL;
      begin
        lHTTP := TIdHTTP.Create(nil);

        TThread.Synchronize(nil,
          procedure
          begin
            Form1.Caption := 'Task Running...';
          end
        );

        try
          lHTTP.ReadTimeout := 30000;
          lHTTP.HandleRedirects := True;
          IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
          IdSSL.SSLOptions.Method := sslvTLSv1;
          IdSSL.SSLOptions.Mode := sslmClient;
          lHTTP.IOHandler := IdSSL;
        Finally
          try
            lHTTP.Get('http://website.com/'+lPath, TStream(nil));
          Finally
            lHTTP.Free;
          end;
        end;

        TThread.Synchronize(nil,
          procedure
          begin
            Memo1.Lines.Add(lPath);
           end
        );

      end
    ).Start;
end;

For an explanation on the background see the explanation in https://stackoverflow.com/a/13349520/101087

Most important parts from the other answer:

Note that variable capture captures variables—not values. If a variable's value changes after being captured by constructing an anonymous method, the value of the variable the anonymous method captured changes too, because they are the same variable with the same storage.

To capture the value of the loop variable, wrap creation of the task in a separate function:

Upvotes: 4

Related Questions