2012-11-05 80 views
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; 
+0

您不應該從主線程(或者所有順便說一句)調用線程執行方法。在沒有正確同步的情況下釋放同一過程中的線程將在線程啓動之前結束線程。 –

+0

是的 - 我錯過了呼叫執行()和不合適的免費。我沒有得到那麼多:) –

+0

@MartinJames,添加到你的答案,你發現失蹤的'繼承'畢竟。 –

回答

4

你忘了呼籲的TThread繼承的構造函數:

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 poinnted出來,沒有必要直接調用的TThread實例的execute()方法 - 線程直接由操作系統執行。

至於釋放它,有一些選擇。按照優先順序排列:

1)不要釋放它。如果您打算在您的應用程序運行期間進行多次下載,請將線程循環到生產者 - 使用者隊列彈出窗口中,以便線程可以重新用於下載更多文件,而不必嘗試釋放它。

2)在try/finally中釋放其內部資源(例如TidHTTP實例)後讓線程實例釋放自身(freeOnTerminate = true)。

-999999)使用OnTerminate處理程序或任何其他可怕的Delphi TThread的東西,如TThread.WaitFor。

+0

修復它,謝謝! – Daniel

+0

TThread.Resume已棄用。你可以做「繼承創建(false)」,然後不需要調用Resume(TThread它將等待,直到在啓動線程之前執行Create中的所有代碼)。 –