• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:スレッドの安全な終了のさせ方)

スレッドの安全な終了のさせ方

このQ&Aのポイント
  • サブスレッドの安全な終了方法とは?
  • イベントオブジェクトを使用して安全な終了処理を行う
  • SendMessageの使用を検討し、デストロイ時にCloseHandleを実行する

質問者が選んだベストアンサー

  • ベストアンサー
  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.4

>SetEvent(hEventObject1);//イベントオブジェクトをシグナルに スレッド終了を判断する場合はスレッドのハンドル自身を見た方が確実です。 HANDLE thread_handle = ::CreateThread(略); (略) ::WaitForSingleObject( thread_handle , INFINITE ); スレッドは終了時にハンドルがシグナル状態になります。 >SendMessage(hMainWnd,....); >//メインウインドウに何かのメッセージを送信 >//なってた時に処理が進まなくなる。 名前から察するにhMainWndはメインスレッドで動いているようですが そのメインスレッドの処理がWaitForSingleObjectによって止まっているのなら処理は返ってきません。 つまりサブスレッドがメインスレッドとなんらかのやりとりをしたいなら、 この時点でメイン側はWaitForSingleObjectで待ってはいけません。 1. Main -> Subに終了前準備しろと通知 2. Sub -> Mainに終了前準備完了を通知 3. Main -> Subに終了しろと通知 4. MainはSubが終了するのをWaitForSingleObjectで待つ。

jacoby2200
質問者

お礼

>つまりサブスレッドがメインスレッドとなんらかのやりとりをしたいなら、 >この時点でメイン側はWaitForSingleObjectで待ってはいけません。 >1. Main -> Subに終了前準備しろと通知 >2. Sub -> Mainに終了前準備完了を通知 >3. Main -> Subに終了しろと通知 >4. MainはSubが終了するのをWaitForSingleObjectで待つ。  解説ありがとうございます。 「段階に分けて」終了処理を行うんですね。 それを受けてメインスレッドのコールバックプロシージャのイベント処理を 書き直しました。 まず「1. Main -> Subに終了前準備しろと通知」 をWM_DESTROYイベントで行っては、そのままメインスレッドが サブの終了を待てずに終わってしまうので その一段階前にWM_CLOSEイベントでこの通知を行い、 この通知を受け取ったサブはスレッドのループを抜けて メインへ「PostMessage」でExit準備完了を通知 (ここでSendMessageを使うと元も子もないので)。 (MY_WM_SUBTHREAD_READY_TO_EXITをメインへポスト)。 メインでMY_WM_SUBTHREAD_READY_TO_EXITを受け取るとそこで WaitForSingleObject(hSubThread,INFINITE); でスレッド終了を待つ。 スレッドが終了したのち、スレッドのハンドルをクローズし DestroyWindowで終了する。 これで無事終了させることができました。 それにしても、段階に分けて終了処理を行うというのはとても 賢明な方法ですね。 教えていただいて、"なるほど"、と初めて気が付きました。 お陰様でプログラムを安心して終了させられそうです。 sha-girlさん、回答ありがとうございました。またよろしくお願いします。 ---------(code)----------- case WM_CLOSE: MessageBox(hWnd,"サブスレッドEXITメッセージポスト。","終了処理",MB_OK); PostThreadMessage(subThread_ID,MY_TM_ABORT_THREAD,0,0);//スレッド中断メッセージのポスト return TRUE; case MY_WM_SUBTHREAD_READY_TO_EXIT: MessageBox(hWnd,"サブスレッドEXIT準備完了を受信。","終了処理",MB_OK); WaitForSingleObject(hSubThread,INFINITE); CloseHandle(hSubThread); MessageBox(hWnd,"スレッド終了処理が完了しました。","終了処理",MB_OK); DestroyWindow(hWnd); return TRUE; ---------(code end)-----------

その他の回答 (3)

  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.3

>「メインスレッドに待ちを作るな」 誰の言葉だかは知りませんがそれは実行中の話だと思います。 マルチスレッドでパフォーマンスを発揮するには如何に非同期で動かすかが重要になってきますが 今回はアプリ終了の際の話ですよね。 1.メインスレッドがサブスレッドに終了する事を通知。  (スマートではないですが例えばグローバルのint型の終了通知フラグで構いません。  Interlocked~系等のAPIを使ってもよいです。) 2.サブスレッドは自ら終了処理。 3.メイン側はWaitForSingleObject/WaitForMultipleObjectでサブスレッド終了を待つ。 4.WinMain(メインスレッド)から抜ける。 でなんら問題ないと思います。 ※これで終了しないのであれば、サブスレッドが終了できていない可能性があります。

参考URL:
http://msdn.microsoft.com/ja-jp/library/cc429227.aspx
jacoby2200
質問者

補足

sha-girlさん、tancoroさん、pdragonさん、回答有難うございます。 サブスレッドには終了を知らせるためにメインからPostThreadMessageで 終了メッセ-ジを投げています。サブスレッドでのループ内でそれを PeekMessageで拾ってループを抜けるようにしています。 (恐らくグローバル変数を用意する方法も形としては同様になるのでは ないかと思うのですが) サブスレッドの処理 ----------------------(code)------------------------- ResetEvent(hEventObject1);//イベントオブジェクトを非シグナルに(スレッド実行中のサイン) for (i=0;i<1000;i++){ ret=PeekMassage(&msg,0,0,0,PM_REMOVE); if (ret!=FALSE){ switch (msg.message){ case MY_TM_ABORT_THREAD://スレッド中断メッセージを受信 //スレッド中断。forループを抜ける } } //* //*ここで時間の掛かる処理を行う。 //* SendMessage(hMainWnd,....); //メインウインドウに何かのメッセージを送信 //↑しかしここでSendMessageを行うと、もしメインがWaitFor~待ちに //なってた時に処理が進まなくなる。 }//for(i)ループ終わり SetEvent(hEventObject1);//イベントオブジェクトをシグナルに ----------------------(code end)----------------------- もちろんループ中、PeekMessageをもっと細かい間隔で行えば待ち時間は 短くなるのですがそうするとループ処理そのものが時間がかかるように なるかと思い、出来るだけ間隔を取って拾う形にしています。 メインの方では終了時こうしています。 ----------------(code)-------------------- case WM_DESTROY: WaitForSingleObject(hEventObject1,10000); //↑ここで待つといつまでも(ここでは仮に10秒限度で)スレッド内のSendMessageが処理できない。 CloseHandle(hSubThread);//スレッドハンドルのクローズ ----------------(code end)---------------- もしメモリリークは気にしなくても良いということなら これらは必要ないのかもとも思いますが。。

  • tancoro
  • ベストアンサー率52% (11/21)
回答No.2

まず、1点目なんですが、 > もしかしたらこれでは場合によっては(サブスレッドがループ処理中だったら)malloc領域が開放されずにリークしてしまうのではないかと思いました。 これに関しては、リークする事はないと思います。メインスレッドもサブスレッドも1つのプロセス下で実行されているに過ぎず、プロセスが終了すれば当然その配下のスレッドで確保されたヒープ領域も解放されます。 しかし・・・・ ループ処理中に強制的にスレッドを終了させるのはあまり良いやり方ではありませんね。 そこで、2点目として綺麗に終わらせるやり方を考える。 まぁ、やり方はいろいろ考えられますが、下記はその1つの例として伺えて下さい。 1.メインスレッドからもサブスレッドからもアクセスできるメモリ領域(ヒープ領域か静的領域)を確保する。例えば、int isDestroy = 0;などとする。 2.メインスレッドにWM_DESTROYメッセージが来たらisDestroyの値をスレッドセーフで1に変更する。 3.サブスレッドでは、ループ処理の中でこのisDestroyを毎回チェックし、値が1であればループを終了するようにする。 このような感じでやるのが一番簡単な方法じゃないでしょうか。 ご参考までに。

jacoby2200
質問者

補足

上記sha-girlさんの欄に自分が書いた補足に訂正があります。 以下のWM_DESTROYのイベントで PostThreadMessageが抜けていました。訂正します。 メインの方では終了時こうしています。 ----------------(code)-------------------- case WM_DESTROY: PostThreadMessage(subThread_ID,MY_TM_ABORT_THREAD,0,0);//スレッド中断メッセージのポスト WaitForSingleObject(hEventObject1,10000); //↑ここで待つといつまでもサブスレッド内のSendMessageが処理できない。 CloseHandle(hSubThread);//スレッドハンドルのクローズ ----------------(code end)---------------- 失礼しました。

  • pdragon
  • ベストアンサー率35% (5/14)
回答No.1

プロセス終了時ならメモリリークは気にしなくてもいいと思いますし、 メイン側での子スレッドのCloseHandleは、子スレッドの動作とは 関係ないでしょうから、「終了を同期させる」ということで。 タイムアウト付きのWaitForSingleObject()のビジーループ内で::Sleep(0)でも 入れればメッセージ処理はされるんじゃないでしょうか。 単に終了を待つだけなら、イベントを使わずとも、GetExitCodeThread()を ビジーループ(::Sleep(100)あたり入れて)回すだけでも良いような気がしますが。