スレッドの安全な終了のさせ方
メインスレッドにてCreateThread命令を使い、あるサブスレッドを作りました。
このサブスレッドは内部でmallocを使い動的に配列領域を確保して
その配列領域をforループ等で「かなり時間の掛かる処理」として繰り返し
アクセスしています。
ループが終了した時に「free」を実行してmalloc領域を開放しています。
アプリ終了時にメインスレッドからこのサブスレッドを終了させるのに
メインウインドウにWM_DESTROYメッセージが送られた時、これまで単に
そこで「CloseHandle(hSubThread);」とだけ書いていたのですが、
もしかしたらこれでは場合によっては(サブスレッドがループ処理中だったら)
malloc領域が開放されずにリークしてしまうのではないかと思いました。
そこでイベントオブジェクトを使い、サブスレッドがループ処理中の
時には非シグナル状態にして、ループが終了しfreeで領域を開放した後
シグナル状態にするということにして、メインスレッドはそれを
WaitForSingleObjectで待つという構造にしました。
ところが「メインスレッドに待ちを作るな」という言葉通り、これでは
上手く行きませんでした。サブスレッドはその時間の掛かる処理の
最中でSendMassage等でメインスレッドの処理を促すような命令を
(例えばその処理の進捗状況を表示するなど)を幾つも行っていたので、
もしWaitFor~でメインを待たせると「サブスレッドの処理も進まなくなり
結果両方がロックして動かなくなってしまう」という悲しい状況に
嵌ってしまうのです。
SendMessageを徹底的に無くすということも考えたのですが、
(例えばPostMessageに書き換えるなどもやってみたのですが、これは
全く意図した動作をしてくれない場合もあり)、別の方法では
どうしても代替できないケースもあって、全て消すというのは
現実的ではないのかもと。。
このようなサブスレッドを安全に終了させるにはどうしたら良いでしょうか?
あるいは単にデストロイ時にCloseHandleとするだけでも良いのでしょうか?
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で待つ。
>つまりサブスレッドがメインスレッドとなんらかのやりとりをしたいなら、
>この時点でメイン側は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)-----------
No.3
- 回答日時:
>「メインスレッドに待ちを作るな」
誰の言葉だかは知りませんがそれは実行中の話だと思います。
マルチスレッドでパフォーマンスを発揮するには如何に非同期で動かすかが重要になってきますが
今回はアプリ終了の際の話ですよね。
1.メインスレッドがサブスレッドに終了する事を通知。
(スマートではないですが例えばグローバルのint型の終了通知フラグで構いません。
Interlocked~系等のAPIを使ってもよいです。)
2.サブスレッドは自ら終了処理。
3.メイン側はWaitForSingleObject/WaitForMultipleObjectでサブスレッド終了を待つ。
4.WinMain(メインスレッド)から抜ける。
でなんら問題ないと思います。
※これで終了しないのであれば、サブスレッドが終了できていない可能性があります。
参考URL:http://msdn.microsoft.com/ja-jp/library/cc429227 …
この回答への補足
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)----------------
もしメモリリークは気にしなくても良いということなら
これらは必要ないのかもとも思いますが。。
No.2
- 回答日時:
まず、1点目なんですが、
> もしかしたらこれでは場合によっては(サブスレッドがループ処理中だったら)malloc領域が開放されずにリークしてしまうのではないかと思いました。
これに関しては、リークする事はないと思います。メインスレッドもサブスレッドも1つのプロセス下で実行されているに過ぎず、プロセスが終了すれば当然その配下のスレッドで確保されたヒープ領域も解放されます。
しかし・・・・
ループ処理中に強制的にスレッドを終了させるのはあまり良いやり方ではありませんね。
そこで、2点目として綺麗に終わらせるやり方を考える。
まぁ、やり方はいろいろ考えられますが、下記はその1つの例として伺えて下さい。
1.メインスレッドからもサブスレッドからもアクセスできるメモリ領域(ヒープ領域か静的領域)を確保する。例えば、int isDestroy = 0;などとする。
2.メインスレッドにWM_DESTROYメッセージが来たらisDestroyの値をスレッドセーフで1に変更する。
3.サブスレッドでは、ループ処理の中でこのisDestroyを毎回チェックし、値が1であればループを終了するようにする。
このような感じでやるのが一番簡単な方法じゃないでしょうか。
ご参考までに。
この回答への補足
上記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)----------------
失礼しました。
No.1
- 回答日時:
プロセス終了時ならメモリリークは気にしなくてもいいと思いますし、
メイン側での子スレッドのCloseHandleは、子スレッドの動作とは
関係ないでしょうから、「終了を同期させる」ということで。
タイムアウト付きのWaitForSingleObject()のビジーループ内で::Sleep(0)でも
入れればメッセージ処理はされるんじゃないでしょうか。
単に終了を待つだけなら、イベントを使わずとも、GetExitCodeThread()を
ビジーループ(::Sleep(100)あたり入れて)回すだけでも良いような気がしますが。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- その他(学校・勉強) この中で間違ってある説明はありますか?詳しい方に教えていただきたいです。 A. 1つのプログラムが複 2 2023/07/14 01:15
- Windows 10 windows10のクリーンインストールが完了出来ない 2 2022/06/04 13:33
- Java Java 南京錠 2 2023/02/04 11:46
- 日本語 「に」について 9 2022/10/25 16:32
- 世界情勢 韓国の尹政府から眺めた日韓関係と成り立たせの可能性は。? 4 2022/05/25 08:06
- Excel(エクセル) エクセルVBA マクロ処理中のポップアップメッセージについて 1 2023/08/04 21:34
- C言語・C++・C# TCP/IP通信時のサーバーからの受信 2 2022/11/23 09:11
- ドライブ・ストレージ HDDの未割り当て領域が認識されない 7 2022/11/27 18:51
- Java Java モンスターブリーダー 1 2023/02/05 09:44
- Windows 8 QTTabBarが無反応になる 2 2023/04/07 14:17
このQ&Aを見た人はこんなQ&Aも見ています
-
これまでで一番「情けなかったとき」はいつですか?
これまでの人生で一番「情けない」と感じていたときはいつですか? そこからどう変化していきましたか?
-
あなたにとってのゴールデンタイムはいつですか?
一週間の中でもっともテンションが上がる「ゴールデンタイム」はいつですか? その逆で、一週間でもっとも落ち込むタイミングでも構いません。 よかったら教えて下さい!
-
遅刻の「言い訳」選手権
よく遅刻してしまうんです…… 「電車が遅延してしまい遅れました」 「歯医者さんが長引いて、、、」 「病院が混んでいて」 などなどみなさんがこれまで使ってきた遅刻の言い訳がたくさんあるのではないでしょうか?
-
いけず言葉しりとり
はんなりと心にダメージを与える「いけず言葉」でしりとりをしましょう。 「あ」あら〜しゃれた服着てはりますな 遠くからでもわかりましたわ
-
とっておきの「まかない飯」を教えて下さい!
飲食店で働く方だけが食べられる、とっておきの「まかない飯」。 働いてらっしゃる方がSNSなどにアップしているのを見ると、表のメニューには出てこない秘密感もあって、「食べたい!!」と毎回思ってしまいます。
-
スレッドの終了の仕方
C言語・C++・C#
-
(マルチスレッド)_beginthreadexに複数の引数を渡す
C言語・C++・C#
-
VC++スレッドの正しい終了のさせかた
C言語・C++・C#
-
-
4
スレッドの終了はどうやるんですか?
C言語・C++・C#
-
5
DWORDの実際の型は何でしょうか
C言語・C++・C#
-
6
CString から LPCTSTRの型に変換
C言語・C++・C#
-
7
ハンドルはポインタか
C言語・C++・C#
-
8
CStringからchar*への型変換について教えてください。
C言語・C++・C#
-
9
エディットボックスの入力制限について
C言語・C++・C#
-
10
画面を強制的に再描画させる方法
C言語・C++・C#
-
11
UpdateData( FALSE); による文字列データの表示更新(VC++6.0)
C言語・C++・C#
-
12
SetDlgItemTextの使い方について
C言語・C++・C#
-
13
SetWindowText関数について
C言語・C++・C#
-
14
C++でアボート(Abort)で処理が強制終了してしまう
C言語・C++・C#
-
15
<unistd.h>をVisualStudioでつかえるようにする
C言語・C++・C#
-
16
メインスレッドのPostMessageとマルチスレッドでの処理
C言語・C++・C#
-
17
CloseHandle()
C言語・C++・C#
-
18
CWnd::EnableWindow()の扱い方
C言語・C++・C#
-
19
C言語での引数の省略方法
C言語・C++・C#
-
20
CString型の文字列連結について
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/22】このサンタクロースは偽物だと気付いた理由とは?
- ・お風呂の温度、何℃にしてますか?
- ・とっておきの「まかない飯」を教えて下さい!
- ・2024年のうちにやっておきたいこと、ここで宣言しませんか?
- ・いけず言葉しりとり
- ・土曜の昼、学校帰りの昼メシの思い出
- ・忘れられない激○○料理
- ・あなたにとってのゴールデンタイムはいつですか?
- ・とっておきの「夜食」教えて下さい
- ・これまでで一番「情けなかったとき」はいつですか?
- ・プリン+醤油=ウニみたいな組み合わせメニューを教えて!
- ・タイムマシーンがあったら、過去と未来どちらに行く?
- ・遅刻の「言い訳」選手権
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
MFC通信プログラムマルチスレッ...
-
マルチスレッドでブレイクポイ...
-
スレッドの監視方法について
-
Windows上で、シグナル(SIGTERM...
-
WaitForSingleObjectの使い方に...
-
マルチスレッド内のループについて
-
CWnd::OnTimerのスレッドの取得
-
VC++スレッドの正しい終了のさ...
-
待機関数(WaitForMultipleObjec...
-
スレッドにて同一メモリの書き...
-
スレッドの安全な終了のさせ方
-
スレッドの終了の仕方
-
C++ GUIのメッセージループ。
-
Macターミナルで実行中のプログ...
-
バックグラウンドのプロセスの...
-
C# シリアル通信でデータ受信...
-
C言語で、メモリを解放しないで...
-
「キャンセル」ボタン付きの処...
-
C#でのbatファイル実行結果取得
-
プロダクションコードとは?
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VC++スレッドの正しい終了のさ...
-
スレッドの監視方法について
-
Windows上で、シグナル(SIGTERM...
-
WaitForSingleObjectの使い方に...
-
スレッドの安全な終了のさせ方
-
スレッドの終了の仕方
-
MFC通信プログラムマルチスレッ...
-
マルチスレッドプログラミング...
-
別スレッドからメインダイアロ...
-
マルチスレッドについて
-
.NetのBackgroundWorkerクラス...
-
スレッドにて同一メモリの書き...
-
別スレッドのデータを受信できない
-
同一スレッドで、ロックをかけ...
-
マルチスレッドの同期方法
-
スレッドの終了はどうやるんで...
-
待機関数(WaitForMultipleObjec...
-
msec単位のWait Timerが作れない!
-
CWnd::OnTimerのスレッドの取得
-
_beginthreadexで生成したスレ...
おすすめ情報