毎度お世話になります。
今、VC++6のMFCでマルチスレッドプログラム作成の準備として
簡単なモデルプログラム(下記)を作っています。
モデルプログラムは、ワーカースレッドのカウントUPをEditBoxに表示する簡単なもの
ですが、スレッド動作中にスレッドを強制終了(OnDestroy)させると
メモリリークが発生します。
Detected memory leaks!
Dumping objects ->
thrdcore.cpp(166) : {63} client block at 0x00374688, subtype 0, 112 bytes long.
a CWinThread object at $00374688, 112 bytes long
Object dump complete.
スレッド動作を一旦中断(OnThreadCan)させてから中止(OnDestroy) させれば
メモリリークは起きません。何か気付いた所がありましたらお願いします。
//----- モデルプログラムの一部
UINT ThreadProc(LPVOID pParam);
CEvent m_pTermThreadsEvent;
//----------スレッド開始-------------------------
void CThreadDlg::OnThread()
{
AfxBeginThread(ThreadProc,GetSafeHwnd());
}
//---------スレッド中断--------------------------
void CThreadDlg::OnThreadCan()
{
m_pTermThreadsEvent.SetEvent(); //停止信号をThreadProcに送る
}
//---------スレッド------------------------------
UINT ThreadProc(LPVOID pParam)
{
int sum = 0;
for (int i = 0; i < 300; i++) {
Sleep(20); //動作遅延
sum++; //加算
SetDlgItemInt((HWND)pParam, IDC_EDIT1, UINT(sum), TRUE); //加算表示
//停止信号チェック
if(::WaitForSingleObject(m_pTermThreadsEvent, 0) == WAIT_OBJECT_0) {
AfxEndThread(0);
break;
}
}
return 0;
}
//-----------スレッド中止-----------------------
void CThreadDlg::OnDestroy()
{
CDialog::OnDestroy();
m_pTermThreadsEvent.SetEvent(); //停止信号をThreadProcに送る
WaitForSingleObject(m_pTermThreadsEvent, INFINITE); //スレッド終了待ち
}
OnDestroy で、基本クラスを呼び出す前に、スレッドの終了待ちをしてみてはどうでしょう。
または、OnDestroy ではなく、OnClose を使ってみるのもいいかもしれません。その場合、
OnOK などにもスレッド終了処理を記述する必要がありますけど。
TADさん 有難うございます。
>スレッドの終了待ちをしてみてはどうでしょう
OnDestroyのWaitForSingleObjectでスレッドの終了待ちをしているつもりなんですが
これでは不十分なんでしょうか。
OnCloseは試してみます。
> WaitForSingleObject(m_pTermThreadsEvent, INFINITE); //スレッド終了待ち
これは m_pTermThreadsEvent が SetEvent されるのを待つだけに見えますが、
このような方法が可能なのでしょうか?
スレッドの終了を待つときは、スレッドハンドルに対してWaitします。
Puppyです。 dairygoodsさん おはようございます。
>スレッドの終了を待つときは、スレッドハンドルに対してWaitします。
そうしないと、いけないんでしょうか?
Puppyもワーカスレッド内でEventオブジェクトをWaitForSingleObjectで
待機しているんですけどぉ。何か問題が起きますか?
よろしくお願いします。m(..)mぺこり
CEvent の基本クラス、CSyncObject の変換演算子によって、HANDLE 値が返されるようです。
コンパイル時にエラーや警告が起こっていないのなら、問題ないと思いますが、HANDLE 型にキャ
ストすれば確実でしょう。
Puppyです。 TADさん お返事ありがとうございます。
にしさん 横から、入ってきてごめんなさい。
>コンパイル時にエラーや警告が起こっていないのなら、問題ないと思いますが、HANDLE
>型にキャストすれば確実でしょう。
CreateEvent APIでイベントオブジェクトを作成してます。
そのイベントオブジェクトのHANDLEをWaitForSingleObjectで待ってます。
問題ないでしょうか?m(..)mぺこり
dairygoodsさん、 Puppyさん、 TADさん 有難うございます。
>スレッドの終了を待つときは、スレッドハンドルに対してWaitします。
確かに、WaitForSingleObjectのHelpをみると「オブジェクトのハンドルを指定します」
とかいてあるので、ハンドルの指定かと思ったのですが、AfxBeginThreadの戻り値は
ポインタなのと、各サイトのQ&Aを見るとこの方式が結構使われているようなので
使用したわけです。
>コンパイル時にエラーや警告が起こっていないのなら
コンパイルではエラーは起きていません。
>HANDLE 型にキャストすれば確実でしょう。
はなはだ申し訳ありませんが、何処をどうゆふうにCASTすればいいのか
ヒントだけでもいただけないでしょうか?
Puppyさん>
Puppy さんも、スレッドの終了処理ですか? それなら、ワーカースレッドで待機するのでは
なく、主スレッドで待機するべきではないでしょうか。主スレッドで、ワーカースレッドの終了を
待機、確認してから、主スレッドの終了処理を行います。
CreateEvent で作成したハンドルを指定しているは、間違っていませんよ。
にしさん>
キャストは普通のキャストと同じです。
WaitForSingleObject((HANDLE)m_pTermThreadsEvent, INFINITE);
とするだけです。
しかし、先ほども言いましたが、やらなくても問題はないはずです。
この位置にブレークポイントを設定して止めてから、ステップイン(F11)してみると、
operator HANDLE() が呼ばれているのが確認できると思います。
CEvent はイベントオブジェクトをカプセル化しただけのもので、コンストラクタで API の
CreateEvent が呼ばれています。
Puppyです。
WaitForSingleObject((HANDLE)m_pTermThreadsEvent, INFINITE); //スレッド終了待ち
こーゆーことでしょうか?
違ってたら、ごめんなさい。m(..)mぺこり
Puppyです。 TADさん お返事ありがとうございます。
>Puppy さんも、スレッドの終了処理ですか?
はい。
>それなら、ワーカースレッドで待機するのでは
>なく、主スレッドで待機するべきではないでしょうか。
>主スレッドで、ワーカースレッドの終了を
>待機、確認してから、主スレッドの終了処理を行います。
そうなんですか。よくわかっていないので。。。
どこかに例題ありませんか。
もうすこしよく勉強して、出直しますぅ。m(..)m
> 確かに、WaitForSingleObjectのHelpをみると「オブジェクトのハンドルを指定します」
> とかいてあるので、ハンドルの指定かと思ったのですが、AfxBeginThreadの戻り値は
> ポインタなのと、各サイトのQ&Aを見るとこの方式が結構使われているようなので
> 使用したわけです。
すみません、ハンドルかポインタかと言うことではなく、
最初のコードを見る限り、m_pTermThreadsEvent は、AfxBeginThread の
戻り値ではなく、単なる CEvent ですよね。
スレッドの終了を待つのであれば、
CWinThread* pThread = AfxBeginThread(...);
...
::WaitForSingleObject(*pThread, INFINITE);
としなければならないと思います。
マルチスレッドを使った、過去の自分のソースを見てみました。
MFC ではなく API だったのですが、イベントオブジェクトを作ることなく、CreateThread で得
たスレッドハンドルを WaitForSingleObject に渡して終了待ちをしていました。
とすると、dairygoods さんの示してくれたコードでいけそうです。
dairygoods さん、ご指摘ありがとうございました。
追加です。
>m_pTermThreadsEvent.SetEvent(); //停止信号をThreadProcに送る
>WaitForSingleObject(m_pTermThreadsEvent, INFINITE); //スレッド終了待ち
これだと確かにおかしいですね。
SetEvent でシグナル状態にしているのですから、次の WaitForSingleObject は無意味になっ
てしまいます。ここをスレッドのハンドルに変えれば正しく動作するはずです。
うーん、どうして最初に気が付かなかったんだろう。
>>m_pTermThreadsEvent.SetEvent(); //停止信号をThreadProcに送る
>>WaitForSingleObject(m_pTermThreadsEvent, INFINITE); //スレッド終了待ち
>これだと確かにおかしいですね。
おかしいでしょうか?
元発言を見ると、問題ないように思えますけど。
SetEventすることによって、スレッド終了のトリガをかけているのではないでしょうか?
INFINITE とすべきではないと思いますが。