全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:3413
推到 Plurk!
推到 Facebook!

重提問題:WaitForMultipleObjects做偵測執行緒結束

尚未結案
jeffxx
一般會員


發表:7
回覆:22
積分:5
註冊:2003-10-10

發送簡訊給我
#1 引用回覆 回覆 發表時間:2005-08-03 11:46:26 IP:61.222.xxx.xxx 未訂閱
問題同 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

發送簡訊給我
#2 引用回覆 回覆 發表時間:2005-08-03 13:39:11 IP:203.70.xxx.xxx 未訂閱
以該篇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

發送簡訊給我
#3 引用回覆 回覆 發表時間:2005-08-03 23:12:23 IP:220.135.xxx.xxx 未訂閱
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

發送簡訊給我
#4 引用回覆 回覆 發表時間:2005-08-03 23:14:29 IP:220.135.xxx.xxx 未訂閱
(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

發送簡訊給我
#5 引用回覆 回覆 發表時間:2005-08-04 11:40:54 IP:203.69.xxx.xxx 未訂閱
看來你沒搞懂 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

發送簡訊給我
#6 引用回覆 回覆 發表時間:2005-08-04 11:51:39 IP:203.69.xxx.xxx 未訂閱
謝謝 jeffxx 和 SouthWind 大大, 讓我有機會再去看 WaitFor....函數, 而且又多ㄧ層認識......
jeffxx
一般會員


發表:7
回覆:22
積分:5
註冊:2003-10-10

發送簡訊給我
#7 引用回覆 回覆 發表時間:2005-08-04 13:31:06 IP:61.222.xxx.xxx 未訂閱
意思是說"能說我要走了的那個人其實還活著"嗎? 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

發送簡訊給我
#8 引用回覆 回覆 發表時間:2005-08-04 14:22:35 IP:61.222.xxx.xxx 未訂閱
附上書上的範列參考 我測了一下好像是有效果的 會不會是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

發送簡訊給我
#9 引用回覆 回覆 發表時間:2005-08-04 14:35:54 IP:203.69.xxx.xxx 未訂閱
能說我要走了當然還活著....< > 引發失效的是死掉的 "把執行緒放進去" 就是跟上帝說, 這個人死了要跟我說一下哦...< > 用 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

發送簡訊給我
#10 引用回覆 回覆 發表時間:2005-08-04 14:59:33 IP:61.222.xxx.xxx 未訂閱
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

發送簡訊給我
#11 引用回覆 回覆 發表時間:2005-08-04 16:22:34 IP:203.69.xxx.xxx 未訂閱
"但是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

發送簡訊給我
#12 引用回覆 回覆 發表時間:2005-08-04 17:59:45 IP:61.222.xxx.xxx 未訂閱
好像不行說~ 這個好像只產生了sleep的效果 WaitForMultipleObjects(6, Pointer(hThread), True, 10000); 只要時間到就會showmessage(counter); WaitForMultipleObjects(6, Pointer(hThread), True, 100); 時間短一點show出來的數字就會不足300 沒辦法達到偵測所的執行緒都結束即秀出結果
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#13 引用回覆 回覆 發表時間:2005-08-04 18:13:47 IP:203.69.xxx.xxx 未訂閱
把你 thread 內有異動到 form1 內元件的部分都備註起來, 不然就跑我列出的程式...    因為, 只要有異動到 VCL 元件的部分都會造成 Dead Lock哦... 像這行就要拿掉
      form1.Memo1.Lines.Add(IntToStr(threadId)   ':'   IntToStr(form1.counter));
發表人 - malanlk 於 2005/08/04 18:20:07
jeffxx
一般會員


發表:7
回覆:22
積分:5
註冊:2003-10-10

發送簡訊給我
#14 引用回覆 回覆 發表時間:2005-08-04 20:56:03 IP:220.135.xxx.xxx 未訂閱
首先先感謝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

發送簡訊給我
#15 引用回覆 回覆 發表時間:2005-08-04 21:13:03 IP:210.68.xxx.xxx 未訂閱
Window XP delphi7 Run 我 Post 上去的程式也會如此嗎?
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#16 引用回覆 回覆 發表時間:2005-08-04 21:21:17 IP:210.68.xxx.xxx 未訂閱
因為 array 是動態配置, 是你自己搞出來的... 用 hThread: array[0..5] of THandle; 試試看...
malanlk
尊榮會員


發表:20
回覆:694
積分:577
註冊:2004-04-19

發送簡訊給我
#17 引用回覆 回覆 發表時間:2005-08-04 21:33:24 IP:210.68.xxx.xxx 未訂閱
這篇講了這麼多觀點, 還解決之前沒解決掉的問題, 只是這個答題得分太難賺了......
系統時間:2024-05-03 5:54:41
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!