如何解決多執行緒重入及鎖定的問題 |
答題得分者是:hao_chih
|
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
原問題:
■【BCB】【問題】如何防止重入? (含問題程式)
http://delphi.ktop.com.tw/topic.php?TOPIC_ID=49478 所衍生的問題。
不論 BCB 或 Delphi 或只有運作的觀念都行,歡迎一起討論。 沒空更新的網頁...
http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
hao_chih
一般會員 ![]() ![]() 發表:15 回覆:25 積分:18 註冊:2003-09-10 發送簡訊給我 |
抱歉加班回家已晚
|
anpino
版主 ![]() ![]() ![]() ![]() 發表:31 回覆:477 積分:231 註冊:2003-01-02 發送簡訊給我 |
|
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
to hao_chih:
感謝您提供的範例,我還需要時間消化一下
------
http://www.ViewMove.com |
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
to hao_chih:
測試結果 OK
------
http://www.ViewMove.com |
jest0024
高階會員 ![]() ![]() ![]() ![]() 發表:11 回覆:310 積分:224 註冊:2002-11-24 發送簡訊給我 |
引言: to hao_chih: 測試結果 OK就因為多執行緒之間會發生行執行緒重複呼叫,所以在重要區段之間得使用互斥元或旗標或事件來控制單一或多個執行緒的呼叫。 第一次建立執行緒時,執行緒會幫你自己建立一個隱形的視窗,所以當多行緒執行VCL元件時,得呼叫Synchronize方法來使用. 當執行緒呼叫Synchronize時會使用SeedMessage傳送訊息給我們所建立的隱形視窗,記得,隱形視窗就如同Form2,與Form1為單一執行緒,以確保VCL的"單一執行",才不會有所謂的重入現象 學而時習之不亦樂乎! |
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: 就因為多執行緒之間會發生行執行緒重複呼叫,所以在重要區段之間得使用互斥元或旗標或事件來控制單一或多個執行緒的呼叫。 第一次建立執行緒時,執行緒會幫你自己建立一個隱形的視窗,所以當多行緒執行VCL元件時,得呼叫Synchronize方法來使用. 當執行緒呼叫Synchronize時會使用SeedMessage傳送訊息給我們所建立的隱形視窗,記得,隱形視窗就如同Form2,與Form1為單一執行緒,以確保VCL的"單一執行",才不會有所謂的重入現象 學而時習之不亦樂乎!對不起,我看不太懂
------
http://www.ViewMove.com |
anpino
版主 ![]() ![]() ![]() ![]() 發表:31 回覆:477 積分:231 註冊:2003-01-02 發送簡訊給我 |
引言: to anpino: 我之前就是使用 CRITICAL_SECTION 來保護,想說,使用 CRITICAL_SECTION 應該就不會重入了,沒想到,在 CRITICAL_SECTION 內如果叫用的函式有使用到 Application->ProcessMessages(),CRITICAL_SECTION 就失效,導致第二個也進入了... 也許我對 CRITICAL_SECTION 還不夠了解,只了解片面的用法,如下:void __fastcall TForm1::TestCS(TObject *Sender) { static CRITICAL_SECTION *pcsTEST=NULL; if(pcsTEST==NULL) { pcsTEST=new CRITICAL_SECTION; InitializeCriticalSection(pcsTEST); } EnterCriticalSection(pcsTEST); try { Label1->Caption=String(Label1->Caption.ToIntDef(0) 1); ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") "test CS...."); TestCS(Sender); // 故意再叫用自己 } __finally { LeaveCriticalSection(pcsTEST); } }我故意直接再叫用自己也是可以的,為什麼? 不是還沒有 LeaveCriticalSection 嗎? 我以為故意叫用的會當在 EnterCriticalSection,可是沒有,為什麼 |
lu
高階會員 ![]() ![]() ![]() ![]() 發表:11 回覆:189 積分:195 註冊:2003-11-19 發送簡訊給我 |
引言:呵呵~~應該說,可能會,也可能不會,原因粉簡單,你THREAD中會呼叫butNewThreadClick()嗎?如果會,那就會發生重入的現象,如果不會就不會 還有一點,個人覺得粉好奇,做啥要在THREAD中呼叫 Application->ProcessMessages(),因為你是在另外一個THREAD之中喔,而你能確定 Application->ProcessMessages()裡面的程式碼,都是THREAD SAFE的嗎?恩...不一定喔 寫MULTITHREAD程式,要先有一個觀念,除非你能確定,你所呼叫的FUNCTION、所操作的OBJECT都是THREAD SAFE,不然都應該視為不是THREAD SAFE的喔void __fastcall TForm1::butNewThreadClick(TObject *Sender) { TMyThread *MyThread = new TMyThread(); MyThread->iMyThreadID = iMaxThreadID; iWaitThreadCount ; }這個事件函式,可由 Timer 叫用或按鈕叫用,而我在使用時,可能由另一個執行緒叫用,會不會也因重入而發生不可預期的結果呢? 如何可以確保紅色部分不會因重入而使得兩個執行緒 iMyThreadID 相同呢? 還是說不可能發生重入? 引言: |
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: pcsTEST已經在EnterCriticalSection時得到使用/呼叫/執行CRITICAL_SECTION內程式的執行權, pcsTEST當然可以再次執行自己, 因為pcsTEST並未LeaveCriticalSection。 也就是說CRITICAL_SECTION內的程式執行權仍然是由pcsTEST獨享。 注意TestCS(Sender); 之後第二次 EnterCriticalSection(pcsTEST);仍然是pcsTEST進入CRITICAL_SECTION, 此時執行權仍在pcsTEST, 所以pcsTEST可以進入。所以,EnterCriticalSection 就不能防重入了... void __fastcall TForm1::TestCS(TObject *Sender) { EnterCriticalSection(pcsTEST); try { Label1->Caption=String(Label1->Caption.ToIntDef(0) 1); ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") "test CS...."); TestCS(Sender); // 故意再叫用自己 } __finally { LeaveCriticalSection(pcsTEST); } }因為這樣也還是不行,那是否要變成 void __fastcall TForm1::TestCS(TObject *Sender,CRITICAL_SECTION *pcsTEST) { EnterCriticalSection(pcsTEST); try { Label1->Caption=String(Label1->Caption.ToIntDef(0) 1); ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") "test CS...."); // TestCS(Sender); // 故意再叫用自己 鐵定重入不再測試 } __finally { LeaveCriticalSection(pcsTEST); } }由叫用者自行 NEW/DELETE CRITICAL_SECTION 如: FUNCATION_OR_TIMER_OR_THERAD_A() { CRITICAL_SECTION *pcsTEST=new CRITICAL_SECTION; InitializeCriticalSection(pcsTEST); TestCS(NULL,pcsTEST); delete pcsTEST; } FUNCATION_OR_TIMER_OR_THERAD_B() { CRITICAL_SECTION *pcsTEST=new CRITICAL_SECTION; InitializeCriticalSection(pcsTEST); TestCS(NULL,pcsTEST); delete pcsTEST; }如此 A,B 兩函式同時亂叫用 TestCS 就會有防重入的效果呢? P.S. 我以前寫的 CRITICAL_SECTION 似乎都白寫了 EnterCriticalSection CALL FUNCTION_A; CALL FUNCTION_B; LeaveCriticalSection而 FUNCTION_A 可能又叫用 FUNCTION_C,FUNCTION_C 可能又叫用 FUNCTION_D,而在 FUNCTION_D 中有使用到 Application->ProcessMessages(),而當發生重入時,系統變得異常, FUNCTION_D用Enter/LeaveCriticalSection夾住。 ------------------------------- 數學系是內功很強(邏輯/分析) 資工系是招式很多(程式技巧) 就像令狐沖VS東方不敗:D ------------------------------- 您指的是這樣嗎? TIMER_OR_THREAD_OR_EVENT() { EnterCriticalSection(AB); CALL FUNCTION_A; CALL FUNCTION_B; LeaveCriticalSection(AB); } FUNCTION_A { CALL FUNCTION_C; } FUNCTION_C { EnterCriticalSection(D); CALL FUNCTION_D; LeaveCriticalSection(D); } FUNCTION_D { FOR LOOP { Application->ProcessMessages(); // DO SOMETHING } }如果是這樣,在 Application->ProcessMessages() 時,TIMER_OR_THREAD_OR_EVENT 又觸發,是否就又重入了呢? 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
jest0024
高階會員 ![]() ![]() ![]() ![]() 發表:11 回覆:310 積分:224 註冊:2002-11-24 發送簡訊給我 |
1.單一執行緒不會存在重入問題,而是存在遞回的問題(你的問題一直在這兒打轉)。
2.使用多執行緒不用刻意呼叫ProcessMessages,因為這個方法是呼叫PeekMessage去讀取系統裡視窗訊息,而TThread並無視窗所以不用去刻意呼叫它。
3.在程式裡頭的Timer所傳送的WM_TIME是由系統發出的一個訊息,就好像你定時按下某個按鍵讓他做事(在fomr裡的單一執行緒)
4.在Form裡使用了多執行緒,就是要做運做較長的工作,把比較簡單的工作放在Form裡,讓Form裡的訊息能持續進行,所以能拿掉ProcessMessages方法
5.所有執行緒要執行VCL都須透過Synchronize
附註:
EnterCriticalSection(D);
LeaveCriticalSection(D);
或互斥元、旗標、事件只為多執行緒存在,而不適用於單一執行緒!! 學而時習之不亦樂乎! 發表人 - jest0024 於 2004/05/07 12:42:44
|
jest0024
高階會員 ![]() ![]() ![]() ![]() 發表:11 回覆:310 積分:224 註冊:2002-11-24 發送簡訊給我 |
引言: 1.單一執行緒不會存在重入問題,而是存在遞回的問題(你的問題一直在這兒打轉)。 2.使用多執行緒不用刻意呼叫ProcessMessages,因為這個方法是呼叫PeekMessage去讀取系統裡視窗訊息,而TThread並無視窗所以不用去刻意呼叫它。 3.在程式裡頭的Timer所傳送的WM_TIME是由系統發出的一個訊息,就好像你定時按下某個按鍵讓他做事(在fomr裡的單一執行緒) 4.在Form裡使用了多執行緒,就是要做運做較長的工作,把比較簡單的工作放在Form裡,讓Form裡的訊息能持續進行,所以能拿掉ProcessMessages方法 5.所有執行緒要執行VCL都須透過Synchronize 附註: EnterCriticalSection(D); LeaveCriticalSection(D); 或互斥元、旗標、事件只為多執行緒存在,而不適用於單一執行緒!! 可能由Timer發送或其他執行緒發送 void __fastcall TForm1::butNewThreadClick(TObject *Sender) { TMyThread *MyThread = new TMyThread(); MyThread->iMyThreadID = iMaxThreadID; iWaitThreadCount ; } 由於上方的程式碼介於VCL之間,所以請透過Synchronize來呼叫,便不會發生 重入的問題。 若是非VCL而是自訂函數,請使用互斥元或EnterCriticalSection之類來確保他單一執行。 //如果不透過以上方法,很難保程式會發生錯亂。 學而時習之不亦樂乎!>"< 想按編輯的,又按成回覆了,金拍謝!! 學而時習之不亦樂乎! 發表人 - jest0024 於 2004/05/07 12:49:33 |
anpino
版主 ![]() ![]() ![]() ![]() 發表:31 回覆:477 積分:231 註冊:2003-01-02 發送簡訊給我 |
引言: 所以,EnterCriticalSection 就不能防重入了...可以防止"其他"thread進入, 而不是防自己咩~~~ 引言: // 程式碼略 如此 A,B 兩函式同時亂叫用 TestCS 就會有防重入的效果呢? P.S. 我以前寫的 CRITICAL_SECTION 似乎都白寫了 |
hao_chih
一般會員 ![]() ![]() 發表:15 回覆:25 積分:18 註冊:2003-09-10 發送簡訊給我 |
引言: 這個事件函式,可由 Timer 叫用或按鈕叫用,而我在使用時,可能由另一個執行緒叫用,會不會也因重入而發生不可預期的結果呢? 如何可以確保紅色部分不會因重入而使得兩個執行緒 iMyThreadID 相同呢? 還是說不可能發生重入?基本上是不會發生的,因為我取得iMaxThreadId是放在主要的程序中(MainTread),主程式是單緒執行,一定要排隊。所以照理說應該不會發生同樣的id情況。 |
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
to lu:
引言: 寫MULTITHREAD程式,要先有一個觀念,除非你能確定,你所呼叫的FUNCTION、所操作的OBJECT都是THREAD SAFE,不然都應該視為不是THREAD SAFE的喔我要如何確認一個 FUNCTION/OBJECT 是 THREAD SAFE 的呢? P.S. 我的程式是 1 個 EXE 載入 50 個左右的 DLL 檔,而每一個 DLL 可能載入 1 到 10 個分身不等,部分 DLL 檔會有特殊元件,每個元件開兩個 ThreadTimer 時間 20ms 及 125ms 分別執行兩個 StateMachine,因為在事件中可能會操作 VCL 或 GUI,所以 ThreadTimer 都使用 Synchronize 的方式叫用 OnThreadTimer 事件,執行時約占 150MB 的記憶體。提供參考
------
http://www.ViewMove.com |
anpino
版主 ![]() ![]() ![]() ![]() 發表:31 回覆:477 積分:231 註冊:2003-01-02 發送簡訊給我 |
引言: to anpino, 對於 CRITICAL_SECTION 的用法,我還是不了解,如果按您的方法,如果我有 10 個 .cpp 都會叫用同一個 FUNCTION_X,而我希望這個 FUNCTION_X 不要重入的話,是每一個 .cpp new 自己的 CRITICAL_SECTION 並 EnterCriticalSection 後再叫用 FUNCTION_X 是這樣子嗎? 對於 CRITICAL_SECTION 是這樣用嗎? 還是 CRITICAL_SECTION 本來不是用在這方面呢? 而我原有的系統使用多個執行緒,每個執行緒也都使用了 Synchronize 的方式執行其 OnTimer 事件,也許是有部分函式用到 Application->ProcessMessages() 的關係,導致從 LOG 上看到有重入的現象,包含 LOG 函式本身都重入了(因為 LOG 字串被截了一半的狀況發生很多次),但在加了 CRITICAL_SECTION 後,由 LOG 函式看來是沒有重入了,而我用的方法就如同前述的 void __fastcall TForm1::TestCS(TObject *Sender) 函式。 但此法以 anpino 版主的說法,及測試程式,似乎說明了無法防重入... ... 對於此部分,還在混亂思考中...吼~~~ 一次回答那麼多人的回應, 辛苦啦~~ 老實說, 我原本以為你要每一個 .cpp new 自己的 CRITICAL_SECTION 並 EnterCriticalSection 後再叫用CRITICAL_SECTION 封裝區的程式, 才宣告CRITICAL_SECTION *pcsTEST[40] = {NULL}; 看來誤會大啦。 CRITICAL_SECTION就是防止"其他"CRITICAL_SECTION進入, 直到原有的CRITICAL_SECTION執行LeaveCriticalSection, "其他"CRITICAL_SECTION才能進入。 也就是說, 下面這個程式"其他"CRITICAL_SECTION絕對無法進入: void __fastcall TForm1::TestCS(TObject *Sender) { static CRITICAL_SECTION *pcsTEST=NULL; static CRITICAL_SECTION *pcsLastTEST=NULL; if(pcsTEST==NULL) { pcsTEST=new CRITICAL_SECTION; InitializeCriticalSection(pcsTEST); } EnterCriticalSection(pcsTEST); try { pcsLastTEST = pcsTEST; pcsTEST = NULL; Label1->Caption=String(Label1->Caption.ToIntDef(0) 1); ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") "test CS...."); } __finally { LeaveCriticalSection(pcsLastTEST); delete pcsLastTEST; pcsLastTEST = NULL; } }對於CRITICAL_SECTION, 可以參考下面文章: 1. Delphi中的线程类 http://www.csdn.net/search_ok.asp 2. UML软件工程组织 北京火龙果软件工程技术中心 Visual C 线程同步技术剖析 http://www.uml.org.cn/net/20043177.htm |
lu
高階會員 ![]() ![]() ![]() ![]() 發表:11 回覆:189 積分:195 註冊:2003-11-19 發送簡訊給我 |
引言: 我要如何確認一個 FUNCTION/OBJECT 是 THREAD SAFE 的呢? P.S. 我的程式是 1 個 EXE 載入 50 個左右的 DLL 檔,而每一個 DLL 可能載入 1 到 10 個分身不等,部分 DLL 檔會有特殊元件,每個元件開兩個 ThreadTimer 時間 20ms 及 125ms 分別執行兩個 StateMachine,因為在事件中可能會操作 VCL 或 GUI,所以 ThreadTimer 都使用 Synchronize 的方式叫用 OnThreadTimer 事件,執行時約占 150MB 的記憶體。提供參考如果你是用Synchronize就不用考慮THREAD SAFE的問題 止於要如何確認一個 FUNCTION/OBJECT 是 THREAD SAFE ,沒有其他方式,問~~廠~~商因為你無法知道他裡面寫了什麼程式 P.S 廠商可能會說不知道,那你...就只好視他為非THREAD SAFE的嘍 引言: 對於 CRITICAL_SECTION 的用法,我還是不了解,如果按您的方法,如果我有 10 個 .cpp 都會叫用同一個 FUNCTION_X,而我希望這個 FUNCTION_X 不要重入的話,是每一個 .cpp new 自己的 CRITICAL_SECTION 並 EnterCriticalSection 後再叫用 FUNCTION_X 是這樣子嗎? 對於 CRITICAL_SECTION 是這樣用嗎? 還是 CRITICAL_SECTION 本來不是用在這方面呢?Multi Thread 程式偶個人喜歡使用Mutex ,原因無他,比較容易防止死結,CRITICAL_SECTION一沒寫好可能會發生死結...因為CRITICAL_SECTION一旦要鎖定時,沒有所謂的TimeOut的設定(例如:最多嘗試10秒,如果還沒鎖定就失敗),而MUTEX有,各位或許會問那有啥差別,偶都是要鎖定阿,差別大了, 第一:你可以在等10秒後,決定是不是要繼續等下去,或是跳過,這是防止死結的好方式.... 第二:TIME OUT時偶可以檢查是否有其他的是發生...例如有人關掉程式了....等等 當然啦CRITICAL_SECTION不是沒好處,好處是,CRITICAL_SECTION不系統核心的物件,所以會比較不耗費資源,處理速度也會比較快... 引言: 而我原有的系統使用多個執行緒,每個執行緒也都使用了 Synchronize 的方式執行其 OnTimer 事件,也許是有部分函式用到 Application->ProcessMessages() 的關係,導致從 LOG 上看到有重入的現象,包含 LOG 函式本身都重入了(因為 LOG 字串被截了一半的狀況發生很多次),但在加了 CRITICAL_SECTION 後,由 LOG 函式看來是沒有重入了,而我用的方法就如同前述的 void __fastcall TForm1::TestCS(TObject *Sender) 函式。看樣子你似乎一直在此問題打轉,在每個執行緒也都使用了 Synchronize 的方式執行其 OnTimer 事件前提下,偶提一個最簡單的解決方式 void __fastcall TForm1::Timer1Timer(TObject *Sender) { static bool ReEnterFlag = false; if (ReEnterFlag) return; ReEnterFlag = true; //Do some thing Application->ProcessMessages(); ReEnterFlag = false; }這樣即可防止『重入』問題,但是請注意必須是在每個執行緒也都使用了 Synchronize 的方式執行其 OnTimer 事件前提下喔 其實Application->ProcessMessages();粉好用,但是也會造成粉多問題,各位要慎用阿,用Application->ProcessMessages()不外乎不想再UI當掉,偶個人覺得在畫面SHOW一行訊息,告訴使用者正在處理資料請稍等,就好了...君不見,M$的程式都是這麼做的(甚至沒任何訊息,只出現漏斗....) 舉各例子:你在處理LOG時,又呼叫Application->ProcessMessages(),這時使用者如果將程式關掉,那會發生啥事?嘿嘿~~試試看吧 ========================= 大家一起快樂寫程式 |
anpino
版主 ![]() ![]() ![]() ![]() 發表:31 回覆:477 積分:231 註冊:2003-01-02 發送簡訊給我 |
|
lu
高階會員 ![]() ![]() ![]() ![]() 發表:11 回覆:189 積分:195 註冊:2003-11-19 發送簡訊給我 |
引言: lu 兄果然是高手!! 佩服佩服! 在下今日有幸遇到高手上課, 真是高興阿!賣安ㄋ共....賣安ㄋ共...< > 這只是...血淚史而已(呵呵~~好像誇張了一點< >) 這都是小弟的一點點經驗而已,偶是寫Winsocket 通訊,所以要寫Multi Thread程式,當然中間遇到粉多問題,差點沒花轟 |
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
to anpino:
//--------------------------------------------------------------------------- void __fastcall TForm1::TestCS(TObject *Sender) { static CRITICAL_SECTION *pcsTEST=NULL; static CRITICAL_SECTION *pcsLastTEST=NULL; if(pcsTEST==NULL) { pcsTEST=new CRITICAL_SECTION; InitializeCriticalSection(pcsTEST); } EnterCriticalSection(pcsTEST); try { pcsLastTEST = pcsTEST; pcsTEST = NULL; ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " start test CS...."); for(int i=1;i<100000000;i ) { int x=(((i*i*i*i i)/i)%i)-i/2; } ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " finish test CS...."); } __finally { LeaveCriticalSection(pcsLastTEST); delete pcsLastTEST; pcsLastTEST = NULL; } } //--------------------------------------------------------------------------- void __fastcall TForm1::Button8Click(TObject *Sender) { LMDHiTimer2->Interval=2000; LMDHiTimer3->Interval=3000; LMDHiTimer2->ThreadPriority=tpNormal; LMDHiTimer3->ThreadPriority=tpNormal; LMDHiTimer2->Enabled=!LMDHiTimer2->Enabled; LMDHiTimer2->Synchronize=false; LMDHiTimer3->Enabled=LMDHiTimer2->Enabled; LMDHiTimer3->Synchronize=false; if(LMDHiTimer2->Enabled) ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") " Now Turn ON Timer...."); else ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") " Now Turn OFF Timer...."); } //--------------------------------------------------------------------------- void __fastcall TForm1::LMDHiTimer2Timer(TObject *Sender) { ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " OnTimerEvent...."); TestCS(Sender); } //--------------------------------------------------------------------------- void __fastcall TForm1::LMDHiTimer3Timer(TObject *Sender) { ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " OnTimerEvent...."); TestCS(Sender); } //---------------------------------------------------------------------------void __fastcall TForm1::TestCS(TObject *Sender) 防不了重入... 是因為第 1 次進入 pcsTEST 已變成 NULL,第 2 次再進入,又再 new 一個新的,照樣進入... LOG 如下: 18:40:40 Now Turn ON Timer.... 18:40:42 LMDHiTimer2 OnTimerEvent.... 18:40:42 LMDHiTimer2 start test CS.... 18:40:43 LMDHiTimer3 OnTimerEvent.... 18:40:43 LMDHiTimer3 start test CS.... 18:40:53 LMDHiTimer2 finish test CS.... 18:40:54 LMDHiTimer3 finish test CS.... 18:40:55 LMDHiTimer2 OnTimerEvent.... 18:40:55 LMDHiTimer2 start test CS.... 18:41:00 LMDHiTimer2 finish test CS.... 18:41:02 LMDHiTimer2 OnTimerEvent.... 18:41:02 LMDHiTimer2 start test CS.... 18:41:09 LMDHiTimer2 finish test CS.... 18:41:11 LMDHiTimer2 OnTimerEvent.... 18:41:11 LMDHiTimer2 start test CS.... 18:41:16 LMDHiTimer2 finish test CS.... P.S. 不知道為什麼 Timer3 就不再觸發了?? to lu: void __fastcall TForm1::Timer1Timer(TObject *Sender) { static bool ReEnterFlag = false; if (ReEnterFlag) return; ReEnterFlag = true; //Do some thing Application->ProcessMessages(); ReEnterFlag = false; }這樣作我知道不會重入,但是等於是少作了,而在我的應用中,不允許少作,每個事件都要處理,所以才需要使用像 hao_chih 所提的自行維護 List 並定時處理的方法。 原本我以為用 CRITICAL_SECTION 就不會重入,它會自己等,事實上沒有我想的那麼簡單,是我把 CRITICAL_SECTION 誤用了,所以,我更想了解 CRITICAL_SECTION 真正的用法。 對於您提到的 Mutex 我一定會找個時間來試試。 我的需求只是 PROCESS_FUNCTION() { { // 可以擋住第二次以上的叫用直到 Main Process Block 作完,再往下作 } ┌────────── │ Main Process Block └────────── }而這個 PROCESS_FUNCTION 可能由不同執行緒叫用。 而在 Main Process Block 中,可能因運算時間太長,而加了 ProcessMessages() 當然,目前我已將程式中所有的 Application->ProcessMessages() 都刪除了,畫面有時幾乎當掉,但程式可正常執行,而我只是想有沒有也可以使用 Application->ProcessMessages() 又可防重入,那就再好不過,如果不行,就是程式中不允許有 Application->ProcessMessages(),以此為主要限制,再去思考如何在畫面上處理,讓客戶可接受。 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
conundrum
尊榮會員 ![]() ![]() ![]() ![]() ![]() ![]() 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
|
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
引言: http://delphi.ktop.com.tw/topic.php?TOPIC_ID=49727 我是來亂的 此文看看 是否有用 如有d sir就po位置把感謝您提供的資訊,內容相當精彩
------
http://www.ViewMove.com |
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
以下的程式算是我原有程式的開發流程...
//--------------------------------------------------------------------------- static CRITICAL_SECTION *pcsTestCS2=NULL; void __fastcall TForm1::TestCS2(TObject *Sender) { if(pcsTestCS2==NULL) { pcsTestCS2=new CRITICAL_SECTION; InitializeCriticalSection(pcsTestCS2); } EnterCriticalSection(pcsTestCS2); try { ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " start test CS2...."); for(int i=1;i<100000000;i ) { if(CheckBox1->Checked // 允許 Application->ProcessMessages in for loop? && i00000==0) Application->ProcessMessages(); int x=(((i*i*i*i i)/i)%i)-i/2; } ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " _end_ test CS2...."); } __finally { LeaveCriticalSection(pcsTestCS2); } } //--------------------------------------------------------------------------- void __fastcall TForm1::Button9Click(TObject *Sender) { LMDHiTimer4->Interval=2000; LMDHiTimer5->Interval=3000; LMDHiTimer4->ThreadPriority=tpNormal; LMDHiTimer5->ThreadPriority=tpNormal; LMDHiTimer4->Enabled=!LMDHiTimer4->Enabled; LMDHiTimer4->Synchronize=CheckBox4->Checked; LMDHiTimer5->Enabled=LMDHiTimer4->Enabled; LMDHiTimer5->Synchronize=CheckBox4->Checked; if(LMDHiTimer4->Enabled) ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") String("Now Turn ON Timer....") ((CheckBox4->Checked)?"[Synchronize]":"[Non Synchronize]") ((CheckBox1->Checked)?"[ProcessMessagesInLoop]":"[NO ProcessMessagesInLoop]")); else ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") "Now Turn OFF Timer...."); } //--------------------------------------------------------------------------- void __fastcall TForm1::LMDHiTimer4Timer(TObject *Sender) { ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " start OnTimerEvent...."); this->Update(); // 因為使用 Synchronize GUI 已無法操作,所以強制重繪看過程 TestCS2(Sender); ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " _end_ OnTimerEvent...."); this->Update(); // 因為使用 Synchronize GUI 已無法操作,所以強制重繪看過程 } //--------------------------------------------------------------------------- void __fastcall TForm1::LMDHiTimer5Timer(TObject *Sender) { ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " start OnTimerEvent...."); this->Update(); // 因為使用 Synchronize GUI 已無法操作,所以強制重繪看過程 TestCS2(Sender); ListBox1->Items->Add(Now().FormatString("HH:NN:SS ") ((TComponent*)(Sender))->Name " _end_ OnTimerEvent...."); this->Update(); // 因為使用 Synchronize GUI 已無法操作,所以強制重繪看過程 } //---------------------------------------------------------------------------我的程式一開始沒有使用 Synchronize 也沒有使用 ProcessMessages 在這種狀況下,CriticalSection 如同預期,可以擋下另一個執行緒的重入: 20:15:57 Now Turn ON Timer....[Non Synchronize][NO ProcessMessagesInLoop] 20:15:59 LMDHiTimer4 start OnTimerEvent.... 20:15:59 LMDHiTimer4 start test CS2.... 20:16:00 LMDHiTimer5 start OnTimerEvent.... <--- 已進入,但沒執行 20:16:05 LMDHiTimer4 _end_ test CS2.... <--- 直到前一個執行完 20:16:05 LMDHiTimer5 start test CS2.... <--- 才接著執行 20:16:05 LMDHiTimer4 _end_ OnTimerEvent.... 20:16:07 LMDHiTimer4 start OnTimerEvent.... 20:16:10 LMDHiTimer5 _end_ test CS2.... 20:16:10 LMDHiTimer4 start test CS2.... 20:16:10 LMDHiTimer5 _end_ OnTimerEvent.... 20:16:13 LMDHiTimer5 start OnTimerEvent.... 20:16:15 LMDHiTimer4 _end_ test CS2.... 20:16:15 LMDHiTimer5 start test CS2.... 20:16:15 LMDHiTimer4 _end_ OnTimerEvent.... 20:16:17 Now Turn OFF Timer.... 20:16:21 LMDHiTimer5 _end_ test CS2.... 20:16:21 LMDHiTimer5 _end_ OnTimerEvent.... 但後來程式愈寫愈大,在執行緒也用了 VCL 中 non-thread-safe 的元件,而發生 Exception,後來才知道要用 Synchronize,但是用了 Synchronize 後,GUI 有時就幾乎當掉,於是在部分函式用了 ProcessMessages,但並沒有仔細去 Check 直到發生問題... 以下的 LOG 可以看出,在 Synchronize/ProcessMessages 的狀況下,CriticalSection 已失效,讓兩個執行緒同時進入了: 20:16:48 Now Turn ON Timer....[Synchronize][ProcessMessagesInLoop] 20:16:50 LMDHiTimer4 start OnTimerEvent.... 20:16:50 LMDHiTimer4 start test CS2.... <---- 4 進入 20:16:51 LMDHiTimer5 start OnTimerEvent.... 20:16:51 LMDHiTimer5 start test CS2.... <---- 5 也進入 20:16:58 LMDHiTimer5 _end_ test CS2.... <---- 5 先作完 20:16:58 LMDHiTimer5 _end_ OnTimerEvent.... 20:17:01 LMDHiTimer5 start OnTimerEvent.... 20:17:01 LMDHiTimer5 start test CS2.... <---- 5 又進入 20:17:07 LMDHiTimer5 _end_ test CS2.... <---- 5 又作完 20:17:07 LMDHiTimer5 _end_ OnTimerEvent.... 20:17:10 LMDHiTimer4 _end_ test CS2.... <---- 4 總算作完一次 20:17:10 LMDHiTimer4 _end_ OnTimerEvent.... 20:17:10 LMDHiTimer5 start OnTimerEvent.... 20:17:10 LMDHiTimer5 start test CS2.... 20:17:12 LMDHiTimer4 start OnTimerEvent.... 20:17:12 LMDHiTimer4 start test CS2.... 20:17:17 Now Turn OFF Timer.... 20:17:19 LMDHiTimer4 _end_ test CS2.... 20:17:19 LMDHiTimer4 _end_ OnTimerEvent.... 20:17:24 LMDHiTimer5 _end_ test CS2.... 20:17:24 LMDHiTimer5 _end_ OnTimerEvent.... 就如同 hao_chih 在 http://delphi.ktop.com.tw/topic.php?TOPIC_ID=49478 所說的 ProcessMessages() 後,其執行的順序就排到後面了,由上述的 LOG 可驗證。 沒空更新的網頁... http://dllee.ktop.com.tw C及指標教學,計算機概論,資訊管理導論... http://dllee.adsldns.org 介紹Shells,LiteStep,GeoShell....
------
http://www.ViewMove.com |
hao_chih
一般會員 ![]() ![]() 發表:15 回覆:25 積分:18 註冊:2003-09-10 發送簡訊給我 |
沒想到一上來就看到一堆高手發表的文章~真讓小弟學到不少東西
|
dllee
站務副站長 ![]() ![]() ![]() ![]() ![]() ![]() 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
萬分感謝 hao_chih 的精彩範例及圖解說明,想必花您不少寶貴的休息時間...
您原來提供的版本已經是不會重入的,又可以有 Application->ProcessMessages 的解決方案,新的版本又可以自行處理 List 內的事件,由排程,可以每次在前者作完時,由 List 中找出最重要需要立即處理的事件來執行,或是按當時系統狀況,也許全部都取消不作,彈性很大,太方便了
------
http://www.ViewMove.com |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |