重提問題:WaitForMultipleObjects做偵測執行緒結束 |
尚未結案
|
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
問題同
http://delphi.ktop.com.tw/topic.php?topic_id=50545
procedure TForm1.Button1Click(Sender: TObject); var i: Integer; reftemp: TCounterThread; hThread: array of THandle; begin if not assigned(cs) then begin cs := TCriticalSection.Create; end; cnt := 0; counter := 0; setlength(hThread, 6); for i := 0 to 5 do begin reftemp := TCounterThread.Create(i); hThread[i] := reftemp.Handle; end; //=====以下這段是抄書上的(win32api系統程式實例入門-博碩)但做不出效果來 //目的:等待到所有執行緒結束才釋放同步物件cs //我run的結果都是while(false),應該一開始是while(true)要執行緒結束時才會while(false)呀 while (WaitForMultipleObjects(6, @hThread, True, 0) = WAIT_TIMEOUT) do begin Sleep(10); end; showmessage(IntToStr(counter)); FreeandNil(cs); end;發表人 - jeffxx 於 2005/08/03 12:16:28 |
chris_shieh
高階會員 發表:46 回覆:308 積分:240 註冊:2004-04-26 發送簡訊給我 |
以該篇Post 討論過程來看
如果你的TCounterThread有呼叫到Main Thread function
則因為
引言: 造成的原因是, TThread 中如果是在 Main Thread 上執行的 Code ,因碰到 Main Thread 執行 Wait Function (TimeOut 設定成 INFINITE)後進入無限循環的等待,造成所有的 Thread DeadLock .. 所以底下這個 Dirty Solution 才得以Work ,但結果是不可預期的, 因為競速的結果,造成只要有一個 Thread 在 WaitForMultipleObjects(OpenDialog1.Files.Count, @Handls, true,0) = WAIT_TIMEOUT 之後,Handle 失效,WaitForMultipleObjects 便會傳回 WAIT_FAILED 跳出迴圈.. 以結果來看,這段 Code 是不正確的..不知道你的情形是不是一樣 @瞭解越多.懂得越少@while WaitForMultipleObjects(OpenDialog1.Files.Count, @Handls, true,0) = WAIT_TIMEOUT do begin Application.ProcessMessages; end; |
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
Orz 都是英文看不懂說我做了如下的測試還是沒有答案可以給點意見嗎?
總共分為四個部分
(1)以event的方式測試WaitForSingleObject showmessage(IntToStr(WaitForSingleObject(ahevent[0],0))); 在執行緒在run時傳回的是258=>WAIT_TIMEOUT 在執行緒結束時傳回的是0=>不知是什麼 (2)以event來做WaitForMultipleObjects (a)主程序 hThread: array of THandle; ahevent:array of THandle; setlength(hThread,6); setlength(ahevent,6); cnt := 0; counter := 0; //建立event for i := 0 to 5 do begin hevent:=CreateEvent(Nil, True, False, 'MyEvent'); ahevent[i]:=hevent; resetEvent(ahevent[i]); end; //建立CriticalSection if not assigned(cs) then begin cs := TCriticalSection.Create; end; //開啟事件 for i := 0 to 5 do begin reftemp := TCounterThread.Create(i); hThread[i] := reftemp.Handle; end; while (WaitForMultipleObjects(6,@ahevent,True,0) = WAIT_TIMEOUT) do begin Sleep(10); end; showmessage(counter); (b)執行緒 unit Unit2; interface uses Classes, SysUtils, SyncObjs,windows; type TCounterThread = class(TThread) private threadId: Integer; { Private declarations } protected procedure Execute; override; public constructor Create(thrid: integer); procedure IncCounter(iCnt: Integer); end; implementation uses Unit1; constructor TCounterThread.Create(thrid: integer); begin inherited Create(False); threadId := thrid; end; procedure TCounterThread.Execute; var i: Integer; begin for i := 1 to 50 do begin form1.cs.Enter; try IncCounter(form1.counter); form1.Memo1.Lines.Add(IntToStr(threadId) ':' IntToStr(form1.counter)); finally form1.cs.Leave; end; end; //結束了把事件設起來 setEvent(ahevent[threadId]); end; procedure TCounterThread.IncCounter(iCnt: Integer); begin sleep(10); form1.counter := iCnt 1; end; end. 結果失敗=>不如預期馬上就showmessage(counter); =>0 並非等到所有執行緒做完才做 ps:無論何時WaitForMultipleObjects(6,@ahevent,True,0)傳回的皆是WAIT_FAILED |
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
(3)我知道WaitForSingleObject傳回的似乎是正確的, 所以我要用WaitForSingleObject的傳回值來自已做類似WaitForMultipleObjects判斷 (a)實作下面的function function TForm1.MyWaitForMultipleObjects(nCount: DWORD; lpHandles: array of THandle): DWORD; var i:Integer; tmp:Integer; begin tmp:=0; for i := 0 to nCount-1 do begin //依測試WaitForSingleObject傳回0就是結束了 //把全部加起來如果是0就是全部結束了 tmp:=tmp WaitForSingleObject(lpHandles[i],0); end; if tmp<>0 then begin tmp:=258; end; Result:=tmp; end; (b)把檢查執行緒改成如下 while (MyWaitForMultipleObjects(6,ahevent) = WAIT_TIMEOUT) do begin Application.ProcessMessages; Sleep(10); end; 結果失敗:看來是有檢查的效果了但不知為何showmessage(counter)出來的不是296就是295偏偏就不是我要的300 ps:但我執行緒結束後我自已去showmessage(counter)是300在同步處理下是有加到300的 (4)我對WaitForMultipleObjects(6,@ahevent,True,0)了解如下 (a)第一個參數是說我有多少事件 (b)第二個參數是事件的array雖然不叫THandle但定義如果 TWOHandleArray = array[0..MAXIMUM_WAIT_OBJECTS - 1] of THandle; PWOHandleArray = ^TWOHandleArray; 名稱不一樣但應該是一樣的東西 (c)第三個參數ture是指全部成立下才過 如果是false只要有一個成立就可以了 (d)第四個參是timeout的時間設定 所以我對 while (WaitForMultipleObjects(6,@ahevent,True,0) = WAIT_TIMEOUT) do begin Sleep(10); end; 解釋如下請每10ms去檢查執行緒結束了沒 如果全部都等於WAIT_TIMEOUT=>沒結束=>就是whlie(true) 如果全部都等於0=>結束了=>就是while(false) 繼續接下來的動作不知為何總是run不出我要的結果是我的觀念錯了嗎? 發表人 - jeffxx 於 2005/08/03 23:21:50 發表人 - jeffxx 於 2005/08/03 23:24:49 |
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
看來你沒搞懂 SouthWind 大大所說 WaitForMultipleObjects(OpenDialog1.Files.Count, @Handls, true,0) = WAIT_TIMEOUT 之後,Handle 失效,WaitForMultipleObjects 便會傳回 WAIT_FAILED 跳出迴圈.. "失效" 就是指 WaitForMultipleObjects 去跟 Handls[i] 問: 你現在是什麼狀態啊? 而上帝(OS) 卻冷冷的說, Handls[i].... 早就掛了.... counter 跑不出 300 的原因就是 所有的 Thread 都是很均勻的分享上帝給他的恩寵, 直到是最後的日子將來臨的時候, 有的靈魂會先走, 偏偏走的時間也差不多....
所以你會看到 counter=296 或 295 是因為有 Thread "失效" 所以迴圈結束
, 而事實上還有 4, 5 個 Thread 還沒嚥氣.....不過還是逃不出宿命, 所以counter 還是會加到 300...... 所以你的改善還差一點... 是不是 讓 每個 Thread 的靈魂脫離肉體那一霎那 "postmessage" 給 主程式說 "我蒙主寵召了" 主程式就會記下, Handls[i] 已逝...合上生死簿,就繼續過他的日子.... 主程式只要在收到最後一個 Handls[j] 的嘆息後....再將征戰的成果 (counter = 300) 秀出來就可以了, 這樣 每個 Thread 都是自由自在的在民主的規定下(Critical Section) 由生到死.... 發表人 - malanlk 於 2005/08/04 11:46:26
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
|
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
意思是說"能說我要走了的那個人其實還活著"嗎?
Sorry資質太差了看了兩三遍才看懂
你的意思是tthread的那個物件已經消失了
所以對著空氣問問題才會發生WAIT_FAILED嗎
以下是我另外的一點小疑問 WaitForMultipleObjects(OpenDialog1.Files.Count, @Handls, true,0) 由於我不清楚把執行緒放進去到底是什麼意思 所以我把它改成以event 為每個執行緒建立一個事件 for i := 0 to 5 do begin hevent:=CreateEvent(Nil, True, False, 'MyEvent'); ahevent[i]:=hevent; resetEvent(ahevent[i]); <====把事件清掉 end; 再執行緒結束時設定事件 procedure TCounterThread.Execute; var i: Integer; begin for i := 1 to 50 do begin form1.cs.Enter; try IncCounter(form1.counter); form1.Memo1.Lines.Add(IntToStr(threadId) ':' IntToStr(form1.counter)); finally form1.cs.Leave; end; end; setEvent(ahevent[threadId]); <===把事件叫起來 end; 接著等待"事件" while (WaitForMultipleObjects(6,@ahevent,True,0) = WAIT_TIMEOUT) do begin Sleep(10); end; 這樣子不能避免掉那個問題嗎發表人 - jeffxx 於 2005/08/04 13:32:20 發表人 - jeffxx 於 2005/08/04 13:43:59 |
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
附上書上的範列參考
我測了一下好像是有效果的
會不會是delphi的問題呢?
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static HANDLE hThread[NUM_OF_THREADS] ; static DWORD threadID ; int wmId, wmEvent ; unsigned int loop ; switch( message ) { case WM_CREATE: InitializeCriticalSection( &critSec ) ; break ; case WM_COMMAND: // user menu wmId = LOWORD( wParam ) ; wmEvent = HIWORD( wParam ) ; switch( wmId ) { case IDM_TEST: sX = 0 ; for( loop = 0 ; loop < NUM_OF_THREADS ; loop ) { hThread[loop] = CreateThread( 0, 0, // create a thread (LPTHREAD_START_ROUTINE)drawThread, (VOID *)hWnd, 0, &threadID ) ; } //DeleteCriticalSection( &critSec ) ; //我在這裡加上刪除CriticalSection結果一開始run就出錯了 while( WaitForMultipleObjects( NUM_OF_THREADS, hThread, TRUE, 0) == WAIT_TIMEOUT ) Sleep( 10 ) ; DeleteCriticalSection( &critSec ) ; //但在WaitForMultipleObjects後面這句是不會造成錯誤 break; case IDM_CLEAR: InvalidateRgn( hWnd, NULL, TRUE ) ; break ; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc( hWnd, message, wParam, lParam ) ; } break ; case WM_DESTROY: DeleteCriticalSection( &critSec ) ; PostQuitMessage(0); break; default: return DefWindowProc( hWnd, message, wParam, lParam ) ; } return 0 ; } |
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
能說我要走了當然還活著....< > 引發失效的是死掉的
"把執行緒放進去" 就是跟上帝說, 這個人死了要跟我說一下哦...< > 用 event 也可以, 那就要改一下
for i := 0 to 5 do begin myEventName := 'MyEvent'+IntToStr(i); hevent:=CreateEvent(Nil, True, False, myEventName ); ahevent[i]:=hevent; resetEvent(ahevent[i]); end;這樣試試... 因為在 Delphi 的 SDK Help CreateEvent 中有一段解釋 lpName 其中有一段 this function requests EVENT_ALL......這段我看不懂, 研究一下再跟我說.... 不然等我有空再說了... 發表人 - |
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
malanlk謝啦~
我把它改成 for i := 0 to 5 do begin eventName:='MyEvent' IntToStr(i); hevent:=CreateEvent(Nil, True, False,PChar(eventName) ); ahevent[i]:=hevent; resetEvent(ahevent[i]); end; 果然run出我要的效果了~原來event的名字不能取一樣呀!! 用以下的方式等待 while (MyWaitForMultipleObjects(6,ahevent) = WAIT_TIMEOUT) do begin Application.ProcessMessages; Sleep(10); end; 但是WaitForMultipleObjects還真是奇怪怎麼做不出這個效果發表人 - jeffxx 於 2005/08/04 15:07:06 |
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
"但是WaitForMultipleObjects還真是奇怪怎麼做不出這個效果" 這句話就有問題了.... WaitForMultipleObjects ㄧ般是期望 以 INFINITE 或 某個 TIMEOUT 時間之後結束, 而在這期間 執行緒是被 OS "冷凍" 起來. 如果像這種 while Wait ForMulti...,0) 的寫法還是會佔 CPU 時間, 所以要用 Sleep 補救..... 像你這個例子, 並沒有 OnTerminate 造成的 Main Thread Lock 問題
(其實不只是 On Terminte, 只要有 更新VCL元件內容的動作也會)
unit Unit3; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, SyncObjs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } cs: TCriticalSection; counter: Integer; end; var Form1: TForm1; implementation {$R *.dfm} uses Unit4; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; reftemp: TCounterThread; hThread: array of THandle; begin if not assigned(cs) then begin cs := TCriticalSection.Create; end; counter := 0; setlength(hThread, 6); for i := 0 to 5 do begin reftemp := TCounterThread.Create(false); hThread[i] := reftemp.Handle; end; WaitForMultipleObjects(6, Pointer(hThread), True, 10000); showmessage(IntToStr(counter)); cs.Free; cs := nil; end; end. unit Unit4; interface uses Classes, SysUtils,SyncObjs; type TCounterThread = class(TThread) private { Private declarations } protected procedure Execute; override; procedure IncCount(iCnt: Integer); end; implementation uses Unit3; procedure TCounterThread.Execute; var i: Integer; begin { Place thread code here } for i := 1 to 20 do begin form1.cs.Enter; try IncCount(form1.counter); finally form1.cs.Leave; end; end; end; procedure TCounterThread.IncCount(iCnt: Integer); begin Sleep(5); form1.counter := iCnt 1; end; end.看10秒內會不會有結果.... |
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
|
jeffxx
一般會員 發表:7 回覆:22 積分:5 註冊:2003-10-10 發送簡訊給我 |
首先先感謝malanlk大大
malanlk實在是太熱心了,ktop有你真好~ 我有看到"(其實不只是 On Terminte, 只要有 更新VCL元件內容的動作也會)"
我有註解了,thread裡只單純加counter而已,run出來的就是我說的結果
我的環境是win2000 delphi7
可以提供你的環境給我參考嗎? sorry malanlk你是正確的你的程式是是ok的,我發現我錯在那了
WaitForMultipleObjects(6, Pointer(hThread), True, 10000); <=你的這句是對的
WaitForMultipleObjects(6, @hThread, True, 10000); <=我用這句是錯的
為什麼呢? 發表人 - jeffxx 於 2005/08/04 21:10:14 發表人 - jeffxx 於 2005/08/04 21:14:54
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
|
malanlk
尊榮會員 發表:20 回覆:694 積分:577 註冊:2004-04-19 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |