如何在辅助线程中使用IdHTTPWork通过汇总下载数据来更新进度条

Saf*_*afa 0 delphi multithreading http download progress-bar

我正在开发一个多线程的下载应用程序.我有一个线程创建了许多线程,可以下载数据.在下载时我需要查看进度条的进度,所以我将最大值设置为文件的大小,并使用IdHTTPWork计算当前下载的数据,我将其添加为线程(辅助线程)的过程.当我的应用程序启动时,主线程创建其他线程下载(在循环中)并设置开始和结束的位置(idhttp.request.range),然后每个线程开始下载,如下所示:

 HTTP.Request.Range := Format('%d-%d',[begin ,end]);
 HTTP.Get(url,fs);
Run Code Online (Sandbox Code Playgroud)

这是secondarythread.work的过程:

 procedure TSecondaryThread.IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount:      Int64);
 begin

  if AWorkMode = wmRead then
    position:= AWorkCount;// position is a global variable
    SendMessage(HWND_BROADCAST,MyMessage, 2,position);
 end;
Run Code Online (Sandbox Code Playgroud)

我不知道这是不是正确的代码,但我找不到另一种解决方案.每个线程都可以使用下载数据的值来增加位置,因此位置将包含瞬时S中的全局下载,我不知道这是否属实.现在我的问题:1-进度与当前下载的数据量不对应; 相反,它增量非常缓慢.2 - 当我添加-ju​​st当我在此过程中添加-Asend消息时,它永远不会停止工作!那么问题是什么?谢谢

Rem*_*eau 7

通过为每个工作线程提供自己的TIdHTTP对象和自己的OnWork事件处理程序,您有了正确的想法.但是您没有正确地将这些状态更新提供给主线程.

使用PostMessage()而不是SendMessage()使您不会减慢工作线程的速度.

您有多个工作线程向主线程发布状态更新,因此请勿使用全局变量来保持进度,当然也不要让工作线程直接更新该变量.每个工作线程应将其当前状态直接放在发布到主线程的消息的参数中,然后主线程可以有一个私有计数器变量,它随每次状态更新而递增.

请勿使用HWND_BROADCAST- 将消息广播到系统中的每个顶级窗口来发布状态更新!仅将消息发布到主线程,通过发布到HWND属于主线程的消息(我建议使用AllocateHWnd()它).

尝试这样的事情:

unit StatusUpdates;

uses
  Windows;

interface

type
  PStatus = ^TStatus;
  TStatus = record
    BytesDownloadedThisTime: Int64;
    BytesDownloadedSoFar: Int64;
    MaxBytesBeingDownloaded: Int64;
 end;

var
  StatusUpdateWnd: HWND = 0;

implementation

end.
Run Code Online (Sandbox Code Playgroud)

uses
  ..., StatusUpdates;

type
  TMainForm = class(TForm)
    ...
  private
    TotalDownloaded: Int64;
    ...
  end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  StatusUpdateWnd := AllocateHWnd(StatusWndProc);
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  if StatusUpdateWnd <> 0 then
  begin
    DeallocateHWnd(StatusUpdateWnd);
    StatusUpdateWnd := 0;
  end;
end;

procedure TMainForm.StartDownload;
begin
  ProgressBar1.Position := 0;
  ProgressBar1.Max := FileSizeToBeDownloaded;
  TotalDownloaded := 0;
  // create download threads...
end;

procedure TMainForm.StatusWndProc(var Message: TMessage);
var
  Status: PStatus;
begin
  if Message.Msg = MyMessage then
  begin
    Status := PStatus(Message.LParam);
    try
      if Status.BytesDownloadedThisTime > 0 then
      begin
        Inc(TotalDownloaded, Status.BytesDownloadedThisTime);
        ProgressBar1.Position := TotalDownloaded;
      end;
      // use Status for other things as needed...
    finally
      Dispose(Status);
    end;
  end else
    Message.Result := DefWindowProc(StatusUpdateWnd, Message.Msg, Message.WParam, Message.LParam);
end;
Run Code Online (Sandbox Code Playgroud)

uses
  ..., StatusUpdates;

type
  TSecondaryThread = class(TThread)
  private
    FTotalBytes: Int64;
    FMaxBytes: Int64;
    procedure PostStatus(BytesThisTime: Int64);
    ...
 end;

procedure TSecondaryThread.PostStatus(BytesThisTime: Int64);
var
  Status: PStatus;
begin
  New(Status);
  Status.BytesDownloadedThisTime := BytesThisTime;
  Status.BytesDownloadedSoFar := FTotalBytes;
  Status.MaxBytesBeingDownloaded := FMaxBytes;
  if not PostMessage(StatusUpdateWnd, MyMessage, 2, LPARAM(Status)) then
    Dispose(Status);
end;

procedure TSecondaryThread.IdHTTPWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
  if AWorkMode = wmRead then
  begin
    FTotalBytes := 0;
    FMaxBytes := AWorkCountMax;
    PostStatus(0);
  end;
end;

procedure TSecondaryThread.IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
  BytesThisTime: Int64;
begin
  if AWorkMode = wmRead then
  begin
    BytesThisTime := AWorkCount - FTotalBytes;
    FTotalBytes := AWorkCount;
    PostStatus(BytesThisTime);
  end;
end;
Run Code Online (Sandbox Code Playgroud)