Thread 關不掉... WaitFor() 回不來? |
答題得分者是:jazz
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
class PACKAGE TxThreadedTimer: public TComponent { friend TxTimerThread; private: bool FEnabled; bool FReallyTerminated; void* FEventHandle; DWORD FInterval; TNotifyEvent FOnTimer; TxTimerThread *FTimerThread; TThreadPriority FThreadPriority; void __fastcall Timer(); public: virtual __fastcall TxThreadedTimer(TComponent *aOwner); virtual __fastcall ~TxThreadedTimer(); virtual void __fastcall UpdateThreadTimer(DWORD dValue, TNotifyEvent eValue, TThreadPriority pValue); virtual void __fastcall TerminateTimer(); __published: __property bool Enabled = {read=FEnabled, write=SetEnabled, default=false}; __property DWORD Interval = {read=FInterval, write=SetInterval, default=1000}; __property TNotifyEvent OnTimer = {read=FOnTimer, write=SetOnTimer, default=NULL}; __property TThreadPriority ThreadPriority = {read=FThreadPriority, write=SetThreadPriority, default=3}; }; class TxTimerThread: public TThread { public: TxThreadedTimer *OwnerTimer; virtual __fastcall TxTimerThread(bool CreateSuspended): TThread(CreateSuspended) {}; virtual __fastcall ~TxTimerThread() {}; virtual void __fastcall Execute(); }; //--------------------------------------------------- __fastcall TxThreadedTimer::~TxThreadedTimer() { FTimerThread->Terminate(); SetEvent(FEventHandle); if(FTimerThread->Suspended) FTimerThread->Resume(); FTimerThread->WaitFor(); CloseHandle(FEventHandle); delete FTimerThread; } //--------------------------------------------------- void __fastcall TxTimerThread::Execute() { OwnerTimer->FReallyTerminated=false; // 正開始執行中 Priority = OwnerTimer->ThreadPriority; DWORD LastStart; DWORD LastDuration = 0; while (!Terminated) { if (LastDuration < OwnerTimer->Interval) WaitForSingleObject(OwnerTimer->FEventHandle, OwnerTimer->Interval - LastDuration); else Application->ProcessMessages(); if (!Terminated) // The thread was not woken { LastStart = GetTickCount(); // Remember the time at which the // synchonisation started Synchronize(OwnerTimer->Timer); LastDuration = GetTickCount() - LastStart; // Calculate the duration // of the synchronisation } } OwnerTimer->FReallyTerminated=true; // 真的已經結束了 }以上是 ThreadTimer 的主要程式碼,在我的應用中,會使用這個 ThreadTimer 約 50 個左右,而在程式要結束時,有時候,某一個 ThreadTimer 會卡在 FTimerThread->WaitFor(); 使得程式無法順利結束。 於是,我將 WaitFor() 改成如下的程式,加上 TimeOut 約 2 秒: __fastcall TxThreadedTimer::~TxThreadedTimer() { FTimerThread->Terminate(); SetEvent(FEventHandle); if(FTimerThread->Suspended) FTimerThread->Resume(); int iTimeOutCount=0; while(this->FReallyTerminated==false && iTimeOutCount<10) { iTimeOutCount ; Application->ProcessMessages(); SetEvent(FEventHandle); if(FTimerThread->Suspended) FTimerThread->Resume(); Application->ProcessMessages(); Sleep(100); FTimerThread->Terminate(); Application->ProcessMessages(); Sleep(100); Application->ProcessMessages(); } CloseHandle(FEventHandle); delete FTimerThread; }但是,在某個 ThreadTimer 發生 TimeOut 時,下一個 Thread 也不會進入 Destroy.不知是不是也已卡在 delete FTimerThread? 對於 Thread 真的是研究不深,只是我一直不明白,為何會 Terminate 不掉呢? 以上的 Code 主要是來自 TThreadedTimer component - Heart 5.0 by Hellix http://www.hellix.com/ 原來 Hellix 是使用 SleepEx(OwnerTimer->Interval - LastDuration, false); 因為在客戶機台這個 ThreadTimer 有問題,在昨天找了一天別人的 ThreadTimer 後,發現有人使用 WaitForSingleObject() 的方式,所以把它改成如上的程式碼。目前還沒在客戶機台上試過,所以不知道 SleepEx 與 WaitForSingleObject 會有什麼差別。 只是同樣的程式,在自己的電腦、公司的電腦、等級高(2G)、等級低(566MHz)、XP、2K、NT4 都可以跑(還是有上述不能關的問題),但是在客戶端 NT4(PIII 1G) 就是連跑都不能跑 而舊的 ><>>介紹>
------
http://www.ViewMove.com | ||
jazz
初階會員 發表:10 回覆:43 積分:35 註冊:2002-06-15 發送簡訊給我 |
|||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: 請問你的Timer都作什麼事情呢?以 StateMachine 的方式「模擬」對機器手臂或其他硬體模組,下指令及等回應。而我的問題主要是:Timer 可以正常執行,但在最後程式要關閉時,在 delete 掉 ThreadTimer 時,才會發生 WaitFor() 回不來的問題。 之前同事的 RS232 模組中的執行緒也是在結束時使用類似的流程: Terminate(); SetEvent(); WaitFor(); 也是會有停在 WaitFor() 的狀況,特別是在 IDE 下執行時會發生,到了客戶機台反而沒問題 不知道是什麼原因會造成 ><>>介紹>
------
http://www.ViewMove.com |
||
jazz
初階會員 發表:10 回覆:43 積分:35 註冊:2002-06-15 發送簡訊給我 |
|||
RaynorPao
版主 發表:139 回覆:3622 積分:7025 註冊:2002-08-12 發送簡訊給我 |
|||
jazz
初階會員 發表:10 回覆:43 積分:35 註冊:2002-06-15 發送簡訊給我 |
|||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言:dllee 你好: 我並沒有試過你的程式碼 只能提供一點點觀念上建議~~參考看看 > > 尤其是當你的程式碼每一行的敘述具有很強的相依性的時候 發生錯誤的情形會愈明顯< > 另外~~>> 備註: >> > -- >>< face="Verdana, Arial, Helvetica"> 謝謝! 對於 Application->ProcessMessages() 我還真的以為可以任意亂加說 以後,我會多多注意一下。 不過,我還是有點不太能理解,或許您有切身的經驗,不知道可不可以說來聽聽呢? 不然,以大部分 >><>>介紹> 發表人 -
------
http://www.ViewMove.com |
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: 我想程式會不會已經發生deadlock的狀態了 WaitFor會使主程式等待執行緒的終結 而你的ThreadTimer是在Synchronize()裡執行 Synchronize呼叫時,程式的主緒被你的執行緒暫時停止 這樣發生互等的情況,可能會有Exception產生吧WaitFor() 是在等執行緒終結沒錯,但如果一個執行緒已經終結了,再 WaitFor() 是否也會發生 deadlock 呢? 而 Synchronize() 我就比較不了解了,按目前看到大部分人的說法是 VCL 主執行緒會在 IDLE 時,才會處理 Synchronize() 內的程序,主要是防止多個執行緒同時要求 VCL(主要是有 Canvas 屬性的元件)更新,若真的發生就會有 Exception。 我之前有試過不要用 Synchronize() ,直接叫用 OnTimer ,結果是真的會有 Exception ,只要乖乖使用 Synchronize()。 引言: 之前我曾經遇過 在執行緒裡處理有關跟畫面有關的東西 例如 Visible = true 或 false 物件 SendToBack() 或 BringToFront() 等 在關閉程式時 會有例外產生 困擾的是 不是每一次都發生 不定期發生 後來把這幾行拿掉就沒再發生了這個問題應該是使用 Synchronize() 後就不會發生了。 沒空更新的網頁...
------
http://www.ViewMove.com |
||
RaynorPao
版主 發表:139 回覆:3622 積分:7025 註冊:2002-08-12 發送簡訊給我 |
引言: 對於 Application->ProcessMessages() 我還真的以為可以任意亂加說 以後,我會多多注意一下。 不過,我還是有點不太能理解,或許您有切身的經驗,不知道可不可以說來聽聽呢? 不然,以大部分 >>>< face="Verdana, Arial, Helvetica"> dllee 你好: 我的確有非常慘痛的經驗~~一開始也是找不到原因 但是後來調整 Application->ProcessMessages() 的位置以後 就不會再發生了 (Access Violation....) 如果 > 而且~~在 > --
------
-- 若您已經得到滿意的答覆,請適時結案!! -- -- 欲知前世因,今生受者是;欲知來世果,今生做者是 -- -- 一切有為法,如夢幻泡影,如露亦如電,應作如是觀 -- |
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: dllee 你好: 我的確有非常慘痛的經驗~~一開始也是找不到原因 但是後來調整 Application->ProcessMessages() 的位置以後 就不會再發生了 (Access Violation....) 如果 > 而且~~在 > -- >>< face="Verdana, Arial, Helvetica"> 在程式碼中,即使我將 Application->ProcessMessages() 都刪除了,還是一樣會有 WaitFor() 回不來的問題 不知道大家都是如何關閉您的 class="code"> __fastcall TxThreadedTimer::~TxThreadedTimer() { FTimerThread->Terminate(); SetEvent(FEventHandle); if(FTimerThread->Suspended) FTimerThread->Resume(); FTimerThread->WaitFor(); CloseHandle(FEventHandle); delete FTimerThread; } 這樣的順序有沒有問題呢? 雖然,我現在已不使用上述的方法(見PS1),但我還是想了解「正確」處理 Thread 的方法及流程,不知,誰可以發表一下心得呢? PS1. 對於我現在的程式已改回 Hellix TThreadedTimer component - Heart 5.0 以 SleepEx() 的方式來處理 ThreadTimer 而不用 WaitForSingleObject() 的方式。原本不能執行的問題是這個 Hellix TThreadedTimer component 與 SPCOMM 不太相容 (這又是另一個問題... ) > 沒空更新的網頁...<><>>介紹>
------
http://www.ViewMove.com |
||
jazz
初階會員 發表:10 回覆:43 積分:35 註冊:2002-06-15 發送簡訊給我 |
|||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: 可否請您試試看下列方式 將Thread的FreeOnTerminate設為true 並且將delete FTimerThread這一行消掉 試試看關於 FreeOnTerminate 我看了一下 VCL 的原始碼, function ThreadProc(Thread: TThread): Integer; var FreeThread: Boolean; begin try Thread.Execute; finally FreeThread := Thread.FFreeOnTerminate; Result := Thread.FReturnValue; Thread.FFinished := True; Thread.DoTerminate; if FreeThread then Thread.Free; EndThread(Result); end; end;是否表示,我的 code 中 void __fastcall TxTimerThread::Execute() { ... OwnerTimer->FReallyTerminated=true; // 真的已經結束了 }當它已執行到最後那一行時,結束時,是否就開始執行 DELPHI 原始碼中紅色的部分。如果是這樣,那我在 __fastcall TxThreadedTimer::~TxThreadedTimer() 如果還使用 FTimerThread (要求 Terminate 或查 Suspended 的狀態)不就會 Exception 了嗎? 還有,您認為有什麼原因是會造成 deaklock 的呢? 之前提到 WaitFor() 也許是一個問題,但如果一個執行緒已經終結了,再 WaitFor() 也許會發生 deadlock !?? function TThread.WaitFor: LongWord; var Msg: TMsg; H: THandle; begin H := FHandle; if GetCurrentThreadID = MainThreadID then while MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_SENDMESSAGE) = WAIT_OBJECT_0 1 do PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) else WaitForSingleObject(H, INFINITE); GetExitCodeThread(H, Result); end;在以上程式中,看不到對於 Terminate 的判斷(或是有但我看不懂 )。 如果真的是,那要如何「安全」、「正確」地使用 >沒空更新的網頁... >
------
http://www.ViewMove.com |
||
jazz
初階會員 發表:10 回覆:43 積分:35 註冊:2002-06-15 發送簡訊給我 |
dllee你好:
我沒有用過waitfor()這個函示
在我用過的程式有使用執行緒都是把這幾行放在程式最後的解構式裡
if(FTimerThread) FTimerThread->Terminate();
我手邊有關執行緒的書也都這樣介紹
因為我曾經用delete的方式,結果會產生例外,所以我才那樣建議你試試看
至於deadlock的問題,純粹只是我的猜測
我有一本書曾經提到這句話
「使用執行緒時,如有用waitfor()要小心使用,可能會發生deadlock」
但是他沒有例子來解說,所以我只是提出來給你參考
小弟真佩服你,還會去看原始VCL的程式,我就有點看不懂delphi的code了
|
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: dllee你好: 我沒有用過waitfor()這個函示 在我用過的程式有使用執行緒都是把這幾行放在程式最後的解構式裡 if(FTimerThread) FTimerThread->Terminate(); 我手邊有關執行緒的書也都這樣介紹 因為我曾經用delete的方式,結果會產生例外,所以我才那樣建議你試試看這個問題,我記得之前我有提問過,但沒人回應... 就是我去修改同事有 Thread 的 code ,因為,我對於有 new 但沒有 delete 的問題很在意,於是多事給他加個 delete ,結果就是 Exception!! 為什麼?為什麼 Thread 不能 delete 呢? (心中唱起蘇芮的「誰能告訴我~~誰能告訴我~~」) 引言: 至於deadlock的問題,純粹只是我的猜測 我有一本書曾經提到這句話 「使用執行緒時,如有用waitfor()要小心使用,可能會發生deadlock」 但是他沒有例子來解說,所以我只是提出來給你參考 小弟真佩服你,還會去看原始VCL的程式,我就有點看不懂delphi的code了要去看 VCL 原始碼也是萬不得以~~有時真的是在想要不就改用 DELPHI 算了!只是工作壓力不許可,改學 DELPHI 也許三個月內都不會有什麼生產力 <>沒空更新的網頁... >
------
http://www.ViewMove.com |
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
體會為何「執行緒(Timer)」「Application->ProcessMessages()」要小心使用。 RaynorPao 兄之前提到
┌───────────────────────────────────
│ dllee 你好:
│ 我並沒有試過你的程式碼
│ 只能提供一點點觀念上建議~~參考看看
│ Thread, Timer, Application->ProcessMessages()
│ 這三個好兄弟~~除非真的有必要或是有很好的 debug 機制的話
│ 我會建議最好不要混用~~因為常常會發生無法預期的狀況
│ 更糟的是~~往往找不到問題到底在哪邊??
│ 尤其是當你的程式碼每一行的敘述具有很強的相依性的時候
│ 發生錯誤的情形會愈明顯
│
│ 另外~~Application->ProcessMessages() 放的位置:
│ 很多人都認為這個敘述非常的好用(的確)
│ 但是一碰到另外兩位好兄弟的時候~~卻變得很危險
│ 建議這個敘述放的位置如下:
│ TimerOrThreadFunction
│ {
│ // 放在第一行
│ // 中間最好不要放
│ // 或放在最後一行
│ }
│
│
│ 備註:
│ Application->ProcessMessages() 建議放在最上層的 function
│ 也就是說如果你的 ThreadFunction 被 TimerFunction 所觸發的話
│ 則建議 ThreadFunction 及其 call 到的所有更下層的 function 中
│ 最好都不要再加 Application->ProcessMessages()
│ 而只加在最上層的 TimerFunction 中(放的位置如上所示)
│
│ -- Enjoy Researching & Developing --
└───────────────────────────────────
在一個多執行緒的程式中,執行緒如何執行、占用 CPU 的狀況,是由系統安排的,
我們的程式中只能設定個執行緒的優先權,但真實的狀況還是由系統決定。
如果,在某一執行緒中,擔心一個大迴圈占用太多 CPU 時間而導致幾乎當機的情況,
此時,如果按單執行緒的程式在迴圈中加入了 Application->ProcessMessages(),
就有可能發生該執行緒要花很久很久比沒有加 Application->ProcessMessages()
可能要多出 2 倍甚至 10 倍或更多的時間(depend on 多少執行緒及與該執行緒之相對優先權)
這是因為當您在執行緒中加入了 Application->ProcessMessages(),等於就是
暫時中止該執行緒,如果主程式的其他執行緒也都很忙,也都在搶 CPU 時,
那這個 Application->ProcessMessages() 可能就會觸發其他同等或較高優先權的
執行緒開始或繼續執行,這也使得原本的大迴圈就得花更久的時間來執行。 正確的處理之道:
□ 在單執行緒的程式中,可以安心使用 Application->ProcessMessages() ,只要系統
其他應用程式不忙,那加了 Application->ProcessMessages() 自然會讓 GUI 順暢。
□ 在多執行緒的程式中,應儘可能少用 Application->ProcessMessages() ,除非您已
確定,此執行緒可以暫時不繼續處理。對於有大迴圈或處理大量資料的執行緒,如果
擔心她會占用太多 CPU 時間,則應設定該執行緒的優先權為 Lower 或 Lowest,如此
就不用擔心其他的執行緒受到影響了。 切身之痛:
在一個主程式與眾多 Plugins 組合的一個機台控制程式中,大約開了 50~60 個執行緒。
在舊機台中,因為當時還不懂執行緒,所以在程式中已有大量的 ProcessMessages(),
在新機台中,因為連接的模組更多、Plugins 也更多了,如果不使用多執行緒,效率非常
差,畫面有時幾乎當掉,客戶無法接受。而在加入多執行緒後,原來在舊機台中,可以
重用的 Plugins 並沒有改寫,或是只有改寫部分,執行時,就會發現有
ProcessMessages() 部分的動作,會比原來的單執行緒要花上 N 倍的時間,可以想像
原本的單執行緒暴增到 50~60 個緒行緒,一個 ProcessMessages() 可能就啟動了不知
多少個執行緒。 沒空更新的網頁...
http://coolsite.to/dllee C及指標教學,計算機概論,資訊管理導論...
http://coolsite.to/ushells 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
我想結案了!但是我想知道以下兩個問題: 1. WaitFor() 回不來的原因?
目前只能由 jazz 提供的經驗,最好不要使用它! 2. 為什麼 Thread 不能 delete ?
為什麼?為什麼?為什麼? 沒空更新的網頁...
http://coolsite.to/dllee C及指標教學,計算機概論,資訊管理導論...
http://coolsite.to/ushells 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
||
jazz
初階會員 發表:10 回覆:43 積分:35 註冊:2002-06-15 發送簡訊給我 |
> 2. 為什麼 Thread 不能 delete ?[/red]
> 為什麼?為什麼?為什麼? 我在C Builder4深入淺出這本書看到一個例子
他在執行緒生成時指定自己的OnTerminate事件,如下
MyThread = new TThread(false);
MyThread->OnTerminate = ThreadDone;
ThreadDown事件如下
void __fastcall TForm1::ThreadDone(TObject* Sender)
{
delete MyThread;
ShowMessage("On Terminate for MyThread Received");
}
其中裡面就有delete Thread的用法
如果是這樣的話,是否意味如果不自己指定OnTerminate事件
BCB自己已經自動隱含清除記憶體的動作呢?
以上只是我的臆測而已...
|
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
|||
kj68215
初階會員 發表:47 回覆:91 積分:27 註冊:2003-08-09 發送簡訊給我 |
雖然這篇很久了~不過小弟最近也在找有關Thread的相關問題...
不過小弟在Hamline University有找到一篇事關於Thread問題
在Wojciech Nałęcz Komornicki's Home Page裡的CPB Mialing List中↓
http://www.hamline.edu/~wnk/cpb/199805/msg00716.html Victor Martinez回覆給Nick的內容中,有提到WaitFor()、FreeOnTerminate跟delete...
有一段挺有意思的..
When you want to kill your thread, just call: ThreadPtr->Terminate();
ThreadPtr->WaitFor();
不知道這些資訊對dllee版大是否有用?!
至於該篇↓
------
----------------------- 請多多指教啦!!^^ |
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
To kj68215, 感謝您提供的資訊。
關於您的問題,可以參考指定事件的文章,如:
http://delphi.ktop.com.tw/topic.php?topic_id=42298
http://delphi.ktop.com.tw/topic.php?topic_id=39065
http://delphi.ktop.com.tw/topic.php?topic_id=33236 最近我的多執行緒系統出了點問題,同時發現之前包大人所說的現象
引言: Application->ProcessMessages() 放的位置: TimerOrThreadFunction { // 放在第一行 // 中間最好不要放 // 或放在最後一行 }還真的是同樣的程式,只是 Application->ProcessMessages() 放前或放後,結果就不同了...唯有放第一行才行!!! 使用時務必要小心。 最近發現 CRITICAL_SECTION 在 Application->ProcessMessages() 的不小心使用下,就會破功了... 這點讓我非常傷腦筋 目前似乎沒有一個好方法可以作到函式不重入,而且還可以使用 > > <>沒空更新的網頁... href="http://dllee.adsldns.org">http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
||
jest0024
高階會員 發表:11 回覆:310 積分:224 註冊:2002-11-24 發送簡訊給我 |
Application->ProcessMessages;
其實是呼叫ProcessMessage(Msg)來讀取訊息.
為啥要放頭跟尾呢!?
loop{
執行程式
Application->ProcessMessages;
(萬一訊息攔到必須要做,就會跳離這個函式了,只造成loop執行一半)
執行程式
} 相對的
放在頭或尾
loop{
Application->ProcessMessages;
(還沒開始就讀取訊息,當跳離程式也比較不要緊)
執行程式
Application->ProcessMessages;
(已經將loop內程式執行完,當跳離程式也不會比中間的要緊吧!?)
} 學而時習之不亦樂乎!
|
||
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |