简体   繁体   English

多线程下载字符串delphi

[英]multithread downloadstring delphi

Function 功能

function DownloadString(AUrl: string): string;
var
  LHttp: TIdHttp;
begin
  LHttp := TIdHTTP.Create;
  try
    LHttp.HandleRedirects := true;
    result := LHttp.Get('http://127.0.0.1/a.php?n='+AUrl);
  finally
    LHttp.Free;
  end;
end;

Boot

procedure TForm1.Button1Click(Sender: TObject);
var
  LUrlArray: TArray<String>;
begin
  LUrlArray := form1.listbox1.Items.ToStringArray;
  TThread.CreateAnonymousThread(
    procedure
    var
      LResult: string;
      LUrl: string;
    begin
      for LUrl in LUrlArray do
      begin
        LResult := DownloadString(LUrl);
        TThread.Synchronize(nil,
        procedure
        begin
          if Pos('DENEGADA',LResult)>0 then
          begin
            Memo1.Lines.Add(LResult);
          end
          else
          begin
            Memo1.Lines.Add(LResult + 'DIE');
          end;
        end
        );
      end;
    end
  ).Start;
end;

Listbox Lines 列表框行

http://127.0.0.1/a.php?n=4984
http://127.0.0.1/a.php?n=4986
http://127.0.0.1/a.php?n=4989

in this case only one thread will download all URL's content but I would like to make it creates a thread for each item... 在这种情况下,只有一个线程将下载所有URL的内容,但我想使其为每个项目创建一个线程...

example: 例:

thread1 - check item1 listbox - http://127.0.0.1/a.php?n=4984
thread2 - check next item 4986
thread3 - check next item 4989

how make this? 如何做到这一点? Is there any way to do this ?, I believe that this method will be more effective. 有什么办法吗?,我相信这种方法会更有效。

In order to create separate threads, you have to bind the url variable value like this: 为了创建单独的线程,您必须像这样绑定url变量值:

procedure TForm1.Button1Click(Sender: TObject);
var
  LUrlArray: TArray<String>;
  LUrl: String;

function CaptureThreadTask(const s: String) : TProc;
begin
  Result := 
    procedure
    var 
      LResult : String;
    begin
      LResult := DownloadString(s);
      TThread.Synchronize(nil,
        procedure
        begin
          if Pos('DENEGADA',LResult)>0 then
          begin
            Memo1.Lines.Add(LResult);
          end
          else
          begin
            Memo1.Lines.Add(LResult + 'DIE');
          end;
        end
        );
    end;
end;

begin
  LUrlArray := form1.listbox1.Items.ToStringArray;
  for LUrl in LUrlArray do
    // Bind variable LUrl value like this
    TThread.CreateAnonymousThread( CaptureThreadTask(LUrl)
    ).Start;
end;

See Anonymous Methods Variable Binding 请参阅匿名方法变量绑定

You can try using ForEach pattern of : 您可以尝试使用 ForEach模式:

Draft is like that: 草稿是这样的:

TMyForm = class(TForm)
private 
   DownloadedStrings: iOmniBlockingCollection;
published
   DownloadingProgress: TTimer;
   MemoSourceURLs: TMemo;
   MemoResults: TMemo;
...
published
   procedure DownloadingProgressOnTimer( Sender: TObject );
   procedure StartButtonClick ( Sender: TObject ); 
.....

private
   property InDownloadProcess: boolean write SetInDownloadProcess;
   procedure FlushCollectedData;
end;

procedure TMyForm.StartButtonClick ( Sender: TObject );  
begin
  DownloadedStrings := TOmniBlockingCollection.Create;

  Parallel.ForEach<string>(MemoSourceURLs.Lines)
     .NumTasks(10) // we do not want to overload computer by millions of threads when given a long list. We are not "fork bomb"

    //  .PreserveOrder - usually not a needed option
     .Into(DownloadedStrings)  // - or you would have to manually seal the container by calling .CompleteAdding AFTER the loop is over in .OnStop option
     .NoWait
     .Execute(
        procedure (const URL: string; var res: TOmniValue)
        var Data: string; Success: Boolean;
        begin
          if my_IsValidUrl(URL) then begin
             Success := my_DownloadString( URL, Data);  
             if Success and my_IsValidData(Data) then begin
                if ContainsText(Data, 'denegada') then
                   Data := Data + ' DIE';
                res := Data;
          end;  
        end
     );

   InDownloadProcess := true;
end;

procedure TMyForm.SetInDownloadProcess(const process: Boolean);
begin
  if process then begin
     StartButton.Hide;
     Prohibit-Form-Closing := true;
     MemoSourceURLs.ReadOnly := true;
     MemoResults.Clear;
     with DownloadingProgress do begin
        Interval := 333; // update data in form 3 times per second - often enough
        OnTimer := DownloadingProgressOnTimer;
        Enabled := True;
     end;
  end else begin
     DownloadingProgress.Enabled := false;
     if nil <> DownloadedStrings then
        FlushCollectedData; // one last time 
     Prohibit-Form-Closing := false;
     MemoSourceURLs.ReadOnly := false;
     StartButton.Show;
  end;
end;

procedure TMyForm.FlushCollectedData;
var s: string; value: TOmniValue; 
begin
  while DownloadedStrings.TryTake(value) do begin
    s := value;
    MemoResults.Lines.Add(s);
  end;

  PostMessage( MemoResults.Handle, .... ); // not SendMessage, not Perform
  // I do not remember, there was something very easy to make the memo auto-scroll to the last line added
end;

procedure TMyForm.DownloadingProgressOnTimer( Sender: TObject );
begin
  if nil = DownloadedStrings then begin
     InDownloadProcess := false;
     exit;
  end;

  FlushCollectedData;

  if DownloadedStrings.IsCompleted then begin
     InDownloadProcess := false;  // The ForEach loop is over, everything was downloaded
     DownloadedStrings := nil;    // free memory 
  end;
end;

PS. PS。 note that the online version of the book is old, you perhaps would have to update it to features in the current version of the sources. 请注意,该书的在线版本较旧,您可能必须将其更新为最新版本的全资源中的功能。

PPS: your code has a subtle error: PPS:您的代码有一个细微的错误:

 for LUrl in LUrlArray do
  begin
    LResult := DownloadString(LUrl);

Given your implementation of DownloadString that means in the case of HTTP error your function would re-return the previous value of LResult again and again and again and.... until the no-error downloading happened. 给定您对DownloadString的实现,这意味着在HTTP错误的情况下,您的函数将一次又一次地等....再次返回LResult的先前值,直到发生无错误的下载为止。 That is why I changed your function definition to be clear when error happens and no output data is given. 这就是为什么我将您的函数定义更改为在发生错误且未提供任何输出数据时清晰可见的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM