WaitForSingleObjectでタイムアウトしてしまう

[戻る]
kai 2002/06/20(木) 13:10:50
お世話になっています。

環境 : Win2000, VC6.0, MFC使用, MDIです。

ワーカスレッドを起動し、プログラムの終了時にイベントをシグナルにすると
WaitForSingleObject の戻り値が WAIT_TIMEOUT になってしまいます。
ワーカスレッド内のSendMessage()はViewに対してユーザー定義メッセージを
送信しています。このSendMessage()をコメントにすると、WaitForSingleObjectは
WAIT_OBJECT_0 を返します。(期待する動作)
SendMessage()に時間がかかるのかと思い、計測してみたら 10 ミリ秒程度なので
問題はなさそうです。
なぜタイムアウトになってしまうのか、原因がわかりません。

識者の方々、アドバイスをお願いします。m(__)m

/////////////////////////////////////////////
// スレッドの起動

m_pThread = AfxBeginThread(Choge::WorkerThread, &m_ThInfo);

/////////////////////////////////////////////
// プログラム終了時の処理

::SetEvent(m_ThInfo.hEvent);
DWORD dwResult = ::WaitForSingleObject(m_pThread->m_hThread, 5000);
●ここでタイムアウトしてしまう

/////////////////////////////////////////////
// Choge::WorkerThread()

// ここで pTh->hEvent は 上記の m_ThInfo.hEvent です
while (::WaitForSingleObject(pTh->hEvent, 0) == WAIT_TIMEOUT) {

  bRet = ::ReadFile(hF, (LPVOID)RcvBuf, byte, &dwReadByte, NULL);
  if (bRet) {
    if ((RcvBuf[0] == m_cFirstRcvChar) && (dwReadByte == byte)) {

      DWORD dwTime1 = GetTickCount();

      ::SendMessage(pTh->hWnd, WM_RCVDATA, (WPARAM)RcvBuf, (LPARAM)
dwReadByte);

      DWORD dwTime2 = GetTickCount();

      DWORD dwTime3 = dwTime2 - dwTime1;  // dwTime3 = 10msec

    }
  }
}

dairygoods 2002/06/20(木) 13:35:15
WaitForSingleObjectを呼び出した側はロックされていますので、
メッセージを投げられても受け取れません。
よってSendMessage呼び出しは終了することがなく、タイムアウトになります。

kai 2002/06/20(木) 13:39:03
dairygoodsさん レスありがとうございます。

>WaitForSingleObjectを呼び出した側はロックされていますので、
>メッセージを投げられても受け取れません。

すみません、意味がわからないので、もう少し詳しく教えて
いただけませんでしょうか?

また、解決策はありますでしょうか?
よろしくお願いします。m(__)m

dairygoods 2002/06/20(木) 14:16:38
終了処理関数の外側でMFCが何をやっているかというと、
こんなイメージです。

while(..) {
  ..
  SendMessage等で届いたメッセージを処理する(A)
  ...
  if (プログラム終了時のイベント) {
    OnEnd();
  } 
}

void OnEnd() {
  ::SetEvent(m_ThInfo.hEvent);
  DWORD dwResult = ::WaitForSingleObject(m_pThread->m_hThread, 5000);
}

WaitForSingleObjectを呼び出すと、スレッドが終了するか
タイムアウトするまで関数から戻らないので、(A)の部分が実行されません。

ワーカースレッドの方はSendMessageでメッセージを送って
受け取ってくれるのをず〜っと待っていますが、
(A)が実行されないのでSendMessage関数は永遠に終了しません。


> また、解決策はありますでしょうか?

タイマーを使ってスレッドの終了を監視すればよいでしょう。
OnTimer(..) {
  ..
  if(::WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_OBJECT_0)
    終了
}

kai 2002/06/20(木) 14:30:40
[[解決]]
dairygoodsさん 早速のレスありがとうございます。

>終了処理関数の外側でMFCが何をやっているかというと、
>こんなイメージです。

よくわかりました。
ご丁寧にありがとうございます。

>> また、解決策はありますでしょうか?
>タイマーを使ってスレッドの終了を監視すればよいでしょう。

こちらもよくわからないのですが
プログラム終了時のイベントで
::SetEvent(m_ThInfo.hEvent);だけおこない、タイマーで

if(::WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_OBJECT_0)
    終了

するということでしょうか?
そうだとすると、この場合終了しないということはないでしょうか?
また、このifを判定するときにすでにスレッドが終了していても
WAIT_OBJECT_0が返ってくるのでしょうか?

よろしくお願いします。

dairygoods 2002/06/20(木) 15:01:44
> また、このifを判定するときにすでにスレッドが終了していても
> WAIT_OBJECT_0が返ってくるのでしょうか?

スレッドが終了していてもWAIT_OBJECT_0を戻します。

ところで、m_pThreadはスレッドが終了すると勝手にdelete
されてしまいますので、スレッドを起動したときに

  m_pThraed->m_bAutoDelete = FALSE;

としておいて、

> if(::WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_OBJECT_0)
  {
    delete m_pThread;
    終了
  }

と自分で削除する必要があります。

kai 2002/06/20(木) 15:21:20
>スレッドが終了していてもWAIT_OBJECT_0を戻します。
>ところで、m_pThreadはスレッドが終了すると勝手にdelete
>(省略)
>と自分で削除する必要があります。

なるほどです。
よくわかりました。ありがとうございます。

ただ、タイマーを使った方法ではうまくいきません。
というのは、CMainFrame::OnClose()でSetEvent()するので
その後にそのまま、CMDIFrameWnd::OnClose()が呼ばれて
終了せざるを得ないからです。

>OnTimer(..) {
>  ..
>  if(::WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_OBJECT_0)
>    終了
>}

判定する前にすでに終了処理がおこなわれてしまいます。

タイマーを使うのが一般的な方法なのでしょうか?

SetEventを使うのをやめて、volatile修飾したフラグで制御したら
うまくいったようなのですが。。。こちらでも大丈夫でしょうか?
よろしくお願いします。

dairygoods 2002/06/20(木) 16:01:06
終了関数内でメッセージ処理してしまうという方法もあります。

void OnClose()
{
 SetEvent(m_ThInfo.hEvent);
 while (WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_TIMEOUT) {
   MSG msg;
   if (PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) {
     AfxGetApp()->PumpMessage();
   }
 }
}

> SetEventを使うのをやめて、volatile修飾したフラグで制御したら
> うまくいったようなのですが。。。こちらでも大丈夫でしょうか?
どちらでも同じような気がします。
たまたまSendMessageからイベントチェックの間に
イベントがセットされれば、ちゃんと終了します。

kai 2002/06/20(木) 16:05:52
>どちらでも同じような気がします。
>たまたまSendMessageからイベントチェックの間に
>イベントがセットされれば、ちゃんと終了します。

ありがとうございました。m(__)m
安心しました、解決です。(^_^;

古いスレッドには追加発言できません。