多線程中COM控件無法Free掉的問題? |
答題得分者是:jow
|
h@visli
資深會員 發表:103 回覆:429 積分:431 註冊:2004-02-13 發送簡訊給我 |
在子線程中,創建了一個ActiveX對象,當線程終止時,需要Free掉這個對象,但程序卻停止在Free這條語句上,請教大家了
單元一: [code delphi] unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Edit1: TEdit; procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation uses Unit2; {$R *.dfm} var MyThd: TMyThread; procedure TForm1.FormCreate(Sender: TObject); begin Randomize; end; procedure TForm1.FormDestroy(Sender: TObject); begin if Assigned(MyThd) then begin Button2Click(nil); end; end; procedure TForm1.Button1Click(Sender: TObject); begin MyThd := TMyThread.Create; end; procedure TForm1.Button2Click(Sender: TObject); begin MyThd.Terminate; MyThd.WaitFor; FreeAndNil(MyThd); end; end. [/code] 單元二: [code delphi] unit Unit2; interface uses classes, SysUtils, Windows, OleCtrls, ActiveX, VCFI; type TMyThread = class(TThread) private procedure RefreshTime; protected procedure Execute; override; public constructor Create; end; implementation uses Unit1; constructor TMyThread.Create; begin FreeOnTerminate := False; inherited Create(False); end; procedure TMyThread.Execute; var vt: TVtChart; //TVtChart is on the 'ActivX' Palette. begin CoInitializeEx(nil, COINIT_MULTITHREADED); VT := TVtChart.Create(nil); while not Terminated do begin Synchronize(RefreshTime); Sleep(35); end; Sleep(100); vt.Free; //<---------The flow will Stop in here CoUninitialize; end; procedure TMyThread.RefreshTime; begin Form1.Edit1.Text := DateTimeToStr(now) IntToStr(Random(991000)); end; end. [/code] 原碼文件:
------
------------------------ 博采眾家之長,奉獻綿薄之力 ------------------------ |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
對不起插個花 因為覺得有趣,所以討論一下。
jow 的程式與 h@visli 主要不同的部分: [code delphi] procedure TMyThread.Execute1; var VT: TVtChart; begin CoInitializeEx(nil, COINIT_MULTITHREADED); try VT := TVtChart.Create(nil); try while not Terminated do begin Synchronize(RefreshTime); Sleep(35); end; Sleep(100); finally FreeAndNil(Vt); end; finally CoUninitialize; end; end; [/code] 除了 try, finally 外,就是使用了 FreeAndNil 的不同, 那是兩個都要用才有效嗎?還是使用 FreeAndNil 就好? 按理 finally 是一定會執行到的,而原本 h@visli 的問題是 Terminate 時執行到 vt.Free 會當,表示那一行有執行到, 所以,只要 FreeAndNil 就可以了(個人簡單的推論,也許有誤)。 但 FreeAndNil 不就是執行 vt.Free 並設 vt:=Nil 嗎? 頂多前面判斷 vt 是否已為 Nil,原 vt.Free 會當除非是當時已是 Nil ? 另外,兩位還有一點不同,就是 Delphi 的版本, h@visli 使用的是 D7 ,而 jow 使用的是 D6 強力推薦 ShareMe 免費網路硬碟 VMASK VMIO-Server/SECS/GEM dllee's blog dllee's StatPlus
------
http://www.ViewMove.com |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
其實 h@visli 所附的程式碼在D6出現一樣的問題,而且也和
是VT.Free 或 FreeAndNil(VT) 沒有關係. 我也是很好奇才稍微 重新實作了一遍,當然依照個人的程式習慣(主要是TThread方面). 程式中除了副站長 所提的差異之外,其實還包括了以下幾點, 先聲明以下差異,我認為應該不致影響 h@visli 和 我的範例出現 不同的執行結果,這也是我好奇的地方. (1)TMyThread.Create(AOwner: TForm1; Index: Integer); 會做這樣的改變是去除 class TMyThread() 中對特定變數的引用, Form1.Edit1.Text := DateTimeToStr(now) IntToStr(Random(991000)); 當然在Form1自動被產生的情況下,上述程式碼不至於產生問題. 純粹是個人 寫程式的習慣. (2)MyThread 釋放的時機: h@visli 兄用的是WaitFor(), 而個人習慣上是等Thread結束Execute中 的迴圈之後,再於其引發的 TThread.OnTerminate() 事件中, 依據 原先設定的 TThread.FreeOnTerminate 去決定是否需要作Free的動作 並將 MyThread 變數重新設為 nil. 這也是個人習慣, 無關程式執行結果. (3) 執行緒變數宣告的地方不同: h@visli 兄將MyThread 宣告在 Unit1 的 implementation 之下, 意謂著 只有在 Unit1 的 implementation 區段下的程式碼才可引用 MyThread變數, 可是就h@visli 兄目前程式碼中使用的範圍,也不至於產生問題. 綜合上述幾項不至於影響執行結果的差異,那麼差異有限的 兩支程式為何有不同的執行結果? 我個人也很好奇.
編輯記錄
jow 重新編輯於 2007-09-17 12:40:44, 註解 無‧
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
是呀, 我眼花了, 沒注意到 .Create 的不同
還是請 h@visli 測試一下吧,如果可以,回覆一下測試結果.... 我真的很好奇耶... ■ 強力推薦 ShareMe 免費網路硬碟 ■ VMASK ■ VMIO-Server/SECS/GEM ■ dllee's blog ■ dllee's StatPlus ■
------
http://www.ViewMove.com |
h@visli
資深會員 發表:103 回覆:429 積分:431 註冊:2004-02-13 發送簡訊給我 |
謝謝兩位的回復,因爲這兩天太忙了所以沒有來得及回復。
我先說一下jow的代碼在我這裡運行的結果(我衹有D7): 1、不管是TMyThread的Execute0還是Execute1,在執行後,點擊btnTerminate0按鈕, 程序會停止在DoOnMyThreadTerminated過程的FreeAndNil(MyThread)這句代碼上, 無法繼續執行: [code delphi] procedure TForm1.DoOnMyThreadTerminated(Sender: TObject); begin if MyThread <> nil then begin Label1.Caption := 'MyThread Terminated, Index = ' IntToStr(MyThread.FIndex); FreeAndNil(MyThread); end; end; [/code] 原因我也正在查找,不知jow在D6上測試是否出現同樣現象? 2、jow的Execute1執行體是可以正常執行的,即FreeAndNil(Vt)與CoUninitialize兩句都 能正常執行。這是因為jow的TMyThread類是直接調用MyThread.Terminate來標志線程終止, 然後而在DoOnMyThreadTerminated中Free線程對象: [code delphi] procedure TForm1.btnTerminate0Click(Sender: TObject); begin if MyThread <> nil then MyThread.Terminate; end; [/code] 但如果是改用 [code delphi] procedure TForm1.btnTerminate0Click(Sender: TObject); begin if MyThread <> nil then begin MyThread.Terminate; MyThread.Waitfor; MyThread.Free; //(即不在DoOnMyThreadTerminated中Free線程對象) end; end; [/code] 這時Execute1也就不能正常執行完成了,會停止在FreeAndNil(Vt);這句上。
------
------------------------ 博采眾家之長,奉獻綿薄之力 ------------------------
編輯記錄
yckuo 重新編輯於 2007-09-17 17:53:41, 註解 套用程式碼高亮顯示‧
|
h@visli
資深會員 發表:103 回覆:429 積分:431 註冊:2004-02-13 發送簡訊給我 |
另外,再說一下我的代碼。
我知道,有兩種Free線程的方法: 1、設置線程的FreeOnTerminate屬性爲False, 然後由我們自己來Free線程對象; 2、設置線程的FreeOnTerminate屬性爲True, 由線程對象在執行完Execute方法後,在 ThreadProc函數中自動Free。 我選用的是第一種方法,因為我的這個線程是在App關閉時在MainFrm的OnDestroy事件中 釋放的。如果採用第二種方法,存在一個問題,即App關閉(主線程)的速度比我這個線程釋放要快, 會造成內存Leak。所以我需要使用WaitFor這個方法,讓主线程來等待子线程Free完畢。 現在的問題就是使用了WaitFor這個方法後,ActiveX控件TVtChart在Free時就死掉了(換用其它ActivX控件都一樣)! 我前天跟蹤至TOleControl的析构函數: destructor TOleControl.Destroy; 發現是停止在這個Destroy裡面。 可惜我的Delphi7今天不知出了什麽問題,今天不能在OleCtrls單元設置斷點了, 盡管我開啓了Use Debug DCUs這個編譯選項。
------
------------------------ 博采眾家之長,奉獻綿薄之力 ------------------------ |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
我的程式碼在D6執行正常.
不知道有沒有其他使用D6的人回應, 是否可以正常執行? 或是有Bug? 補充說明,目前FreeOnTerminate 設為 False. 也就是說釋放MyThread的動作是在TForm1.DoOnMyThreadTerminated()中, [code delphi] constructor TMyThread.Create(AOwner: TForm1; Index: Integer); begin inherited Create(False); FOwner := AOwner; FIndex := Index; FreeOnTerminate := False; end; [/code] [code delphi] procedure TForm1.DoOnMyThreadTerminated(Sender: TObject); begin if MyThread <> nil then begin Label1.Caption := 'MyThread Terminated, Index = ' IntToStr(MyThread.FIndex); FreeAndNil(MyThread); end; end; [/code] |
wameng
版主 發表:31 回覆:1336 積分:1188 註冊:2004-09-16 發送簡訊給我 |
|
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
<textarea class="delphi" rows="10" cols="60" name="code">
procedure TMyThread.Execute1;
var
VT: TVtChart;
begin
////////// CoInitializeEx(nil, COINIT_MULTITHREADED);
try
VT := TVtChart.Create(nil);
try
while not Terminated do
begin
Synchronize(RefreshTime);
Sleep(35);
end;
Sleep(100);
finally
FreeAndNil(Vt);
end;
finally
////////// CoUninitialize;
end;
end; procedure TForm1.btnTerminate1Click(Sender: TObject);
var
Timeout: Cardinal;
IsTimeout: Boolean;
begin
if MyThread <> nil then
begin
MyThread.OnTerminate := nil;
IsTimeout := False;
Timeout := GetTickCount 5000; MyThread.Terminate;
while not (MyThread.Terminated or IsTimeout)do
begin
IsTimeout := GetTickCount > timeout;
Application.ProcessMessages;
Sleep(10);
end; if IsTimeout then
Label1.Caption := 'MyThread Termination Timeout'
else if MyThread.Terminated then
Label1.Caption := '(NEW)MyThread Terminated, Index = '
IntToStr(MyThread.FIndex); MyThread.Free;
MyThread := nil; end;
end; procedure TForm1.btnTerminate2Click(Sender: TObject);
begin
if MyThread <> nil then
begin MyThread.OnTerminate := nil;//取消 MyThread.Terminate;
MyThread.WaitFor; if MyThread.Terminated then
Label1.Caption := '(NEW)MyThread Terminated, Index = '
IntToStr(MyThread.FIndex); MyThread.Free;
MyThread := nil; end;
end; initialization CoInitializeEx(nil, COINIT_MULTITHREADED); finalization CoUninitialize; </textarea>
依wameng 建議, 修改程式碼. 在D6測試, 三個按鍵執行正常. |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
哇,如果是這樣的解法... 在 BCB 下就麻煩了,
因為 BCB 每個 unit 沒有 initialization, finalization 可用 看了 jow 的 code 讓我更了解 Thread 的用法 感謝分享。 ■ 強力推薦 Seednet ShareMe 至少 2G 免費網路硬碟, 最大特點:放檔後不使用不會砍檔 ■
------
http://www.ViewMove.com |
h@visli
資深會員 發表:103 回覆:429 積分:431 註冊:2004-02-13 發送簡訊給我 |
wameng版主的代碼果然有效!但這是爲什麽呢?
另外, Jow老兄的FreeAndNil(MyThread); 這句(btnTerminate0Click)我在D2007下調試時,也與D7下一樣停止執行下去。 我分析了一下Classes裡的線程執行過程: [code delphi] function ThreadProc(Thread: TThread): Integer; var FreeThread: Boolean; begin try if not Thread.Terminated then try Thread.Execute; except Thread.FFatalException := AcquireExceptionObject; end; finally FreeThread := Thread.FFreeOnTerminate; Result := Thread.FReturnValue; Thread.DoTerminate; Thread.FFinished := True; SignalSyncEvent; if FreeThread then Thread.Free; EndThread(Result); end; end; [/code] 請注意 Thread.DoTerminate這句,Jow兄的DoOnMyThreadTerminated代碼就是由 Thread.DoTerminate 調用,且在主線程中執行,而Jow兄在這裡FreeAndNil(MyThread),而ThreadProc函數後面的代碼還要用到 MyThread這個對象,所以我們不能在Thread.DoTerminate這裡就Free線程對象的。 另外,Jow兄的btnTerminate1Click與btnTerminate2Click中都有先MyThread.Terminate;再判斷MyThread.Terminated 。其實這種判斷是沒必要的,因為MyThread.Terminate就是把線程的Terminated屬性置爲True(主線程中), 所以沒有必要再判斷。 ===================引 用 wameng 文 章=================== initialization CoInitializeEx(nil, COINIT_MULTITHREADED); finalization CoUninitialize; end. 不要懷疑就是這樣。 我也不清楚原因。
------
------------------------ 博采眾家之長,奉獻綿薄之力 ------------------------ |
jow
尊榮會員 發表:66 回覆:751 積分:1253 註冊:2002-03-13 發送簡訊給我 |
這是D6版的code
不同點在於順序問題 Thread.FFinished := True; Thread.DoTerminate; if FreeThread then Thread.Free;//////FreeOnTerminate := False; 另外 D7 多了一項 SignalSyncEvent; <textarea class="delphi" rows="10" cols="60" name="code">function ThreadProc(Thread: TThread): Integer; var FreeThread: Boolean; begin {$IFDEF LINUX} if Thread.FSuspended then sem_wait(Thread.FCreateSuspendedSem); {$ENDIF} try if not Thread.Terminated then try Thread.Execute; except Thread.FFatalException := AcquireExceptionObject; end; finally FreeThread := Thread.FFreeOnTerminate; Result := Thread.FReturnValue; Thread.FFinished := True; Thread.DoTerminate; if FreeThread then Thread.Free; {$IFDEF MSWINDOWS} EndThread(Result); {$ENDIF} {$IFDEF LINUX} // Directly call pthread_exit since EndThread will detach the thread causing // the pthread_join in TThread.WaitFor to fail. Also, make sure the EndThreadProc // is called just like EndThread would do. EndThreadProc should not return // and call pthread_exit itself. if Assigned(EndThreadProc) then EndThreadProc(Result); pthread_exit(Pointer(Result)); {$ENDIF} end; end; </textarea>
編輯記錄
jow 重新編輯於 2007-09-18 11:01:08, 註解 無‧
|
h@visli
資深會員 發表:103 回覆:429 積分:431 註冊:2004-02-13 發送簡訊給我 |
|
mitchellhu
一般會員 發表:23 回覆:53 積分:15 註冊:2007-06-12 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |