いけず言葉しりとり

こんにちは。

WaitForSingleObjectを使って、スレッドがシグナル状態になるまで待機するために、以下のようにコーディングしました。
WaitForSingleObject( threadHandle, INFINITE );

ところが上記だと、スレッドが正常にシグナル状態にならなかった場合に、永遠に待ち続けフリーズしてしまう可能性があること以下のサイトで知りました。
http://hitokuso.kicks-ass.org/progtips.html#1

while( ::WaitForSingleObject( threadHandle, 50 ) == WAIT_TIMEOUT )
{
MSG msg;
::GetMessage( &msg, NULL, 0, 0 );
if( !IsDialogMessage( &msg ) )// ダイアログ内の場合
{
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
}

上記のようにwhileループで回し、タイムアウトだったらメッセージを処理しているようなのですが、タイムアウト時間として50ミリ秒を指定しているので、一瞬だけ待ってすぐにメッセージ処理を継続しているような気がします。これでは、スレッドがシグナル状態になるまで待たないような気がするのですが…
まだまだ知識不足のため、見当違いな疑問を持っているのかもしれませんが、どなたかアドバイス・ご教授をお願いいたします。

A 回答 (4件)

上記のサンプルには2点間違いがあります。


1. GetMessage() 関数はなんらかしらのメッセージが届くまで制御を戻しません。そのため、スレッドが終了したのにメッセージが届かなくなるまでは処理がブロックされてしまいます。代わりにメッセージが「あれば」取りに行く PeekMessage() 関数を使用します。
2. メッセージ処理を行いながら待機する場合には WaitForSingleObject() 関数ではなく、MsgWaitForMultipleObjects() 関数を使用します。

そのため、質問にありますスレッドがシグナル状態になるまで待つようにするには、
::GetMessage( &msg, NULL, 0, 0 );

while( ::PeekMessage( &msg, NULL, 0, 0 ) )
に変更すればよいかと思います。

なお、2. の問題については、タイムアウト時間をより長く設定するために必要な処理ですので、気が向いたら対策していただければよいと思います。対策方法は若干複雑になりますが、以下のようになります。

::WaitForSingleObject( threadHandle, 50 )

::MsgWaitForMultipleObjects(1, &threadHandle, FALSE, INFINITE, QS_ALLINPUT )
に変更して、
* MsgWaitForMultipleObjects()の戻り値が 0 の場合はスレッドが終了したため、ループを抜けます。
* 戻り値が 1 の場合は、PeekMessage() 以下の処理を実行します。
* それ以外の場合には、エラーが発生していますのでエラー処理を実行してループを抜けます。
    • good
    • 2

#1の方の回答の通りですが、少し補足します。



URLの説明は、スレッド中でSendMessage()を使っているとそのメッセージを受け取ってやらないとスレッドが終了しないので、スレッドを終了させるためメッセージを受け取ってやる例として質問にあるコードをあげてあるようです。

実際は、そのスレッドが何をしているかによります。
直接SendMessage()は呼んでいなくても、間接的に呼ばれる可能性もありますから、あまり単純にはこうしなくてはならないとは言えないと思います。
まあ、とりあえずは、画面の表示などの変更があれば使っていると思えばいいかなと。


WaitForSingleObject()はシグナル状態になった場合と、TIMEOUTした場合で戻り値が違います。
質問にあるソースではタイムアウトの場合にwhileループを実行しますから、
結果スレッドがシグナル状態になると戻り値が変化してwhileループを抜けるわけです。
つまり、スレッドを待っていることになります。
whileループの中は実行されてますから待っていないと言えなくもないですが、TIMEOUTさせることは待ちきれずに待つのをやめることですからね。
    • good
    • 0

>永遠に待ち続けフリーズしてしまう可能性があること


それはただのプログラム上のバグです。
WaitForSingleObject(WaitForMultipleObjects)
はCPUに少ない負担で待ち状態にできるのがメリットです。
ですからシグナル状態まで必ず待たせたいのなら
INFINITEを指定してください。

WaitForSingleObject( threadHandle, 50 )
これだと50ミリ秒間隔で通過します。

>一瞬だけ待ってすぐにメッセージ処理を継続しているような気がします
コードを見るとそれを意図しているコードにしかみえません。

while( ::WaitForSingleObject( threadHandle, 50 ) == WAIT_TIMEOUT )
これだと
while(true){
Sleep(50);
(略)
}
と似たような動作になります。

シグナル状態で通過させたいなら
::WaitForSingleObject( threadHandle, 50 ) == WAIT_OBJECT_0
です。
    • good
    • 0

このコードの場合、WaitForSingleObject()を一瞬で通過するのは確かですが、


同様にメッセージ処理部分も一瞬で通過しています。つまりwhile文によって、

WaitForSingleObject()→メッセージ処理→WaitForSingleObject()→メッセージ処理→・・・

というループ処理が延々繰り返されている事になります。
スレッド側がシグナル状態になれば、WaitForSingleObject()でそれを検知しますから
ループを抜けることができるわけです。

WaitForSingleObject()でINFINITEを指定した場合との違いはメッセージを処理するかどうかだけで
シグナル状態になるまで待機する事には変わりありませんから、
もしスレッド側が正常終了しなかった場合には、このコードでも無限ループします。
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています


おすすめ情報

このQ&Aを見た人がよく見るQ&A