0

このスレッドを使用しようとすると、「スレッド エラー: ハンドルが無効です (6)」が表示され続けますが、問題がわかりません。できれば助けてください、ありがとう!

stackoverlow は、私がこれを十分に説明していないと不平を言っています。そのため、エラーなしでコンパイルされるスレッド クラスを作成しましたが、execute で呼び出すと、正常に実行されているように見えますが、デストラクタでこのエラーがスローされます。

  tdownloadthread = class(tthread)
    private
      furl,
      ffilename,
      fmsg: string;
      fdl: tidhttp;
      readings,bpstotal,avgbps: int64;
      fpercent: word;
      fsuccess,fcanceled: boolean;
      start: tdatetime;
      fspeed,fremaining: string;
    public
      constructor create(url,filename: string);
      destructor destroy; override;
      procedure execute; override;
      procedure DlWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
      procedure cancel;

      property success: boolean read fsuccess;
      property canceled: boolean read fcanceled;
      property speed: string read fspeed;
      property percent: word read fpercent;
      property remaining: string read fremaining;
      property msg: string read fmsg;
  end;


constructor tdownloadthread.create(url,filename: string);
begin
  fsuccess:=false;
  fcanceled:=false;

  fdl:=tidhttp.Create(nil);
  fdl.OnWork:=dlwork;
  fdl.HandleRedirects:=true;

  furl:=url;
  ffilename:=filename;

  freeonterminate:=false;
end;

destructor tdownloadthread.Destroy;
begin
  fdl.Free;
end;

procedure tdownloadthread.cancel;
begin
  fdl.Disconnect;
  fcanceled:=true;
end;

procedure tdownloadthread.DlWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
var
  Http: TIdHTTP;
  ContentLength: Int64;
  i,h,m,esec,sec,rsec,bps: Integer;
  f: tdatetime;
begin
  Http := TIdHTTP(ASender);
  ContentLength := Http.Response.ContentLength;

  if (Pos('chunked', LowerCase(Http.Response.ResponseText)) = 0) and
     (ContentLength > 0) then
  begin
    fpercent := trunc(100*(AWorkCount / ContentLength));

    esec := secondsbetween(now, start);

    if (avgbps > 0) then
     begin
       i:=contentlength div avgbps;
       f:=incsecond(now, i);
       rsec:=secondsbetween(now, f);

       sec:=rsec - esec;

       h:=sec div 3600;
       m:=sec div 60 - H * 60;
       sec:=sec - (H * 3600 + M * 60) ;
    end;

    if (esec > 0) then
      bps:=(aworkcount div esec)
       else
      bps:=0;

    inc(readings);
    inc(bpstotal, bps);
    avgbps:=bpstotal div readings;

    if (avgbps / 1024 < 1024) then
      fspeed:=format('%2f KB/s', [avgbps / 1024])
       else
      fspeed:=format('%2f MB/s', [(avgbps div 1024) / 1024]);

    fremaining:=format('%s m %s s', [zero(m), zero(secno)]);
  end;
end;

procedure tdownloadthread.Execute;
var fn,cd: string;
    data: tmemorystream;
begin
  try
    start:=now;

    data:=tmemorystream.Create;

    fdl.Get(furl, data);
    cd:=fdl.Response.ContentDisposition;
    if (pos('filename=', cd) > 0) then
      fn:=extractfilepath(paramstr(0))+copy(cd, pos('=', cd)+1, length(cd))
         else
      fn:=ffilename;
    data.SaveToFile(fn);
    ffilename:=fn;
    fsuccess:=true;

    data.Free;
  except
    on e: exception do
      begin
        fsuccess:=false;
        fmsg:=e.Message;
      end;
  end;
end;

procedure testdownload;
var downloadthread: tdownloadthread;
begin
  downloadthread:=tdownloadthread.create('http://www.someurl.com/filename.old',extractfilepath(paramstr(0))+'filename.new');
  downloadthread.Execute;
  if downloadthread.canceled then showmessage('canceled');
  if downloadthread.success then showmessage('success');
  if not downloadthread.success then showmessage('error: '+downloadthread.msg);
  downloadthread.Free;
end;
4

1 に答える 1

4

TThread の継承された ctor を呼び出すのを忘れました:

constructor tdownloadthread.create(url,filename: string);
begin
  inherited create(true); // construct TThread
  fsuccess:=false;
  fcanceled:=false;

  fdl:=tidhttp.Create(nil);
  fdl.OnWork:=dlwork;
  fdl.HandleRedirects:=true;

  furl:=url;
  ffilename:=filename;

  freeonterminate:=false;
  resume; // actually run thread
end;

また、@LU RD が指摘したように、TThread インスタンスの Execute() メソッドを直接呼び出す必要はありません。スレッドは OS によって直接実行されます。

解放に関しては、いくつかの選択肢があります。優先順:

1) まったく解放しないでください。アプリの実行中に複数のダウンロードを行う場合は、スレッドをプロデューサー/コンシューマー キュー ポップの周りにループさせて、スレッドを再利用して、スレッドを解放しようとする悲惨さなしに、より多くのファイルをダウンロードできるようにします。

2) try/finally で内部リソース (TidHTTP インスタンスなど) を解放した後、スレッド インスタンスにそれ自体を解放させます (freeOnTerminate=true)。

-999999) OnTerminate ハンドラまたは TThread.WaitFor のような他の恐ろしい Delphi TThread を使用します。

于 2012-11-05T17:41:15.953 に答える