お世話になっています。
環境 : 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
}
}
}
WaitForSingleObjectを呼び出した側はロックされていますので、
メッセージを投げられても受け取れません。
よってSendMessage呼び出しは終了することがなく、タイムアウトになります。
dairygoodsさん レスありがとうございます。
>WaitForSingleObjectを呼び出した側はロックされていますので、
>メッセージを投げられても受け取れません。
すみません、意味がわからないので、もう少し詳しく教えて
いただけませんでしょうか?
また、解決策はありますでしょうか?
よろしくお願いします。m(__)m
終了処理関数の外側で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)
終了
}
dairygoodsさん 早速のレスありがとうございます。
>終了処理関数の外側でMFCが何をやっているかというと、
>こんなイメージです。
よくわかりました。
ご丁寧にありがとうございます。
>> また、解決策はありますでしょうか?
>タイマーを使ってスレッドの終了を監視すればよいでしょう。
こちらもよくわからないのですが
プログラム終了時のイベントで
::SetEvent(m_ThInfo.hEvent);だけおこない、タイマーで
if(::WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_OBJECT_0)
終了
するということでしょうか?
そうだとすると、この場合終了しないということはないでしょうか?
また、このifを判定するときにすでにスレッドが終了していても
WAIT_OBJECT_0が返ってくるのでしょうか?
よろしくお願いします。
> また、この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;
終了
}
と自分で削除する必要があります。
>スレッドが終了していてもWAIT_OBJECT_0を戻します。
>ところで、m_pThreadはスレッドが終了すると勝手にdelete
>(省略)
>と自分で削除する必要があります。
なるほどです。
よくわかりました。ありがとうございます。
ただ、タイマーを使った方法ではうまくいきません。
というのは、CMainFrame::OnClose()でSetEvent()するので
その後にそのまま、CMDIFrameWnd::OnClose()が呼ばれて
終了せざるを得ないからです。
>OnTimer(..) {
> ..
> if(::WaitForSingleObject(m_pThread->m_hThread, 0)==WAIT_OBJECT_0)
> 終了
>}
判定する前にすでに終了処理がおこなわれてしまいます。
タイマーを使うのが一般的な方法なのでしょうか?
SetEventを使うのをやめて、volatile修飾したフラグで制御したら
うまくいったようなのですが。。。こちらでも大丈夫でしょうか?
よろしくお願いします。
終了関数内でメッセージ処理してしまうという方法もあります。
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からイベントチェックの間に
イベントがセットされれば、ちゃんと終了します。
>どちらでも同じような気がします。
>たまたまSendMessageからイベントチェックの間に
>イベントがセットされれば、ちゃんと終了します。
ありがとうございました。m(__)m
安心しました、解決です。(^_^;