質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

1261閲覧

メインスレッドのコントロールを参照しているサブスレッドがアプリケーション終了時にコントロールの値の参照を正常に行えない[マルチスレッド][C++/MFC]

notgoodpg

総合スコア37

MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/12/27 08:36

編集2017/12/28 06:33

タイトルをより適切に変更しました
###前提・実現したいこと
MFCアプリケーションを閉じる動作を行う際、コントロールが参照不能になる前に処理を行いたい。
~~ 閉じる動作を行う際にコントロールが参照不能になる以前の状態で処理を行えるイベントが知りたい。~~
閉じる動作を行う際にコントロールが参照不能になる現象を解消、または回避したい。

  • 作成中のアプリケーションはダイアログベースのMFCアプリケーションです。
  • OnDestroy()イベント関数内で、CDialog::OnDestroy()が走る前にGetExitCodeThread()で別スレッドの終了コードを取得して別スレッドの終了を確認してからアプリケーションを終了する処理を実装しようとしています。
  • 別スレッドは1つです。
  • 別スレッド内ではループで処理を行い、その中ではメインスレッドのコントロールの値を参照しています。
  • メインスレッドではGetExitCodeThread()を実行する前に別スレッドのループを終わらせるフラグを立てて、別スレッドが終了したときにGetExitCodeThread()でexitCode = 0 となるようにしいます。
  • OnDestroy()を使用しているのはブレークポイントを設置して観察して、OnClose()や~CMyAppDlg()よりも早く実行されることを確認したためです。

実際のソースコードを提示できないため抽象的な質問になり申し訳ありませんが、アドバイスなど頂けたら幸いです。
よろしくお願いいたします。

###発生している問題・エラーメッセージ
OnDestory()に処理が移って別スレッドのループ終了フラグがtrueになったあと
別スレッドが最後のループに入ったときに、スライドコントロールの位置を参照しようと( CSliderCtrl::GetPos() )すると
エラーメッセージが出るわけでも突然プロセスが終了するわけでもなく、GetExitCodeThread()の値を監視しているループから抜けられずアプリケーションが終了しなくなってしまいます。
別スレッドではCSliderCtrl::GetPos()の処理に入ってから処理が進まなくなります。

###該当のソースコード
このサイトなどを参照するとOnDestroy()より先にOnClose()が走るようなので修正しました。
OnDestroy内に記述でもよいとのご指摘があったので元に戻し、ご指摘のあった部分を修正しました。

c++

1void CMyAppDlg::OnDestroy() 2{ 3 // デストロイする前にカメラスレッドを終了させる 4 WaitForSingleObject( this->m_mutex, 0); 5 this->m_exitFlagAnotherThd = true; 6 ReleaseMutex( this->m_mutex); 7 8 DWORD exitCode = -1; 9 do{ 10 // this->m_hAnotherThread は ResumeThread( this->m_hAnotherThread);によって開始済みのスレッド 11 if( !GetExitCodeThread( this->m_hAnotherThread, &exitCode)) 12 { 13 TerminateThread( this->m_hAnotherThread, E_FAIL); 14 break; 15 } 16 }while( exitCode == STILL_ACTIVE); 17 // mutex 18 CloseHandle(this->m_mutex); 19 20 CDialog::OnDestroy(); 21 22 // TODO: ここにメッセージ ハンドラ コードを追加します。 23} 24// 別スレッドの実行内容 25DWORD WINAPI CMyAppDlg::ExecThread() 26{ 27 WaitForSingleObject( this->m_mutex, 0); 28 this->m_exitAnotherThd = false; // ループ終了フラグ 29 ReleaseMutex( this->m_mutex); 30 // メインループ(の代わりのWhileループ) 31 int i =0; 32 while( !this->m_exitAnotherThd){ // m_exitAnotherThd == falseになるまで待機 33 // ここから先に進まない 34 i = ((CSliderCtrl *)FromHanlde(this->m_hwnd)->GetDlgItem(IDC_SLIDER1))->GetPos(); // ウィンドウハンドラを介して取得 35 } 36 i = ((CSliderCtrl *)FromHanlde(this->m_hwnd)->GetDlgItem(IDC_SLIDER1))->GetPos(); 37 // i = ((CSliderCtrl *)GetDlgItem(IDC_SLIDER1))->GetPos(); 38 return S_OK; 39}
  • OnDestory()関数が呼び出されるまではiにスライダの値が正常に入ります。

以上のコードは実際に実装しているものとは異なります。雰囲気だけでも伝われば幸いです。

###補足情報(言語/FW/ツール等のバージョンなど)
VisualStudio2008 SP1
C++/MFC
Windows7/64bit SP1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

OnDestroy内で、ループしてるため、ウィンドウのメッセージループが回ってないためではないでしょうか。

通常、サブスレッドから、直接CSliderCtrl::GetPos()などは呼べないはずなので、そこいらあたりがどうなっているのかわかりませんが、そこ近辺に問題があると思います。

投稿2017/12/28 04:09

Harahira

総合スコア243

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

notgoodpg

2017/12/28 04:29

ご回答ありがとうございます。 値の取得方法を見直してみます。
guest

0

ベストアンサー

OnDestroyのタイミングでコントロールにアクセスすることは問題ありません。

なお、別スレッド側のコードが不明ですが、以下が気になります。

まず、別スレッド終了待ちループにおいて、別スレッドがS_OK以外で終了、あるいはGetExitCodeThread失敗した時、無限ループに陥ります。
この状態に陥っている可能性はないでしょうか?

ちなみに私なら以下のようにSTILL_ACTIVEチェックするループを組みます。

C++

1do{ 2 if( !GetExitCodeThread( this->m_hAnotherThread, &exitCode)){ 3 TerminateThread( this->m_hAnotherThread, E_FAIL); 4 break; 5 } 6}while( exitCode == STILL_ACTIVE);

また、別スレッドにおいて、ダイアログ内で宣言したコントロール変数を介してGetPos()しているのであれば

ウィンドウ ハンドルのマップ

通常、スレッドは自分自身が作成した MFC オブジェクトしかアクセスできません。 これは、Windows ハンドルの一時マップとパーマネント マップがスレッド ローカル ストレージに保持されており、複数のスレッドから同時にアクセスできないためです。

とあるように、その動作は保証されません。
別スレッド側ではウインドウハンドル経由でアクセスされているのであれば問題ありませんが。

別スレッド処理について追記

えーと。

  • メンバ関数をそのままAfxBeginThreadのスレッド関数として渡す
  • (したがって)別スレッド側でthisポインタでダイアログインスタンスにアクセスする

のはまずいです。詳細はAfxBeginThread member functionなどで検索してみてください。
この問題には目をつぶったうえでGetPosが無反応になる原因としてはHarahiraさんの指摘のとおりと推察します。
GetPosは、内部ではスライダーコントロールに対してのメッセージ処理が行われていますが、メイン側のダイアログでメッセージループ処理が行われていないため、処理が止まっていると思います。

とりあえず以下のような関数を定義しOnDestroy内での終了待ちループ内で呼び出せばGetPos動作すると思われます(動作未検証)

C++

1void PumpMessage( void) 2{ 3 MSG msg; 4 while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){ 5 TranslateMessage(&msg); 6 DispatchMessage(&msg); 7 } 8}

が、これは悪手です。

根本的には、以下のように処理を見直したほうがよいです。

  • メイン側とサブ側の両方からアクセスできるスライダー位置変数を用意する。(m_exitFlagAnotherThd のような)
  • メイン側にスライダーの位置変更ハンドラを追加し、ハンドラ関数では位置変数値を更新する。
  • サブ側では位置変数値の参照のみを行う。

基本的には、サブ側スレッドは

  • メイン側で設定された変数値の参照だけを行う。
  • メイン側への何らかの通知は、ユーザー定義メッセージをPostなりでおこなう

ほうが、今回のような問題が発生せず、構造が簡易になります。

投稿2017/12/28 01:58

編集2017/12/28 06:40
can110

総合スコア38252

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

notgoodpg

2017/12/28 04:26 編集

ご回答ありがとうございます。 ご指摘頂いた箇所を改善しました。STILL_ALIVEチェックを知らなかったので大変助かりました。ありがとうございます。 実装方法が間違えているのか、現象は解決しませんでした・・・。 /*今リンク先に改善方法についての記述があるのに気づきました。現在試してみています。 */ /*ウィンドウハンドル経由でのアクセスの仕方について、GetDlgItem()関数を利用しての取得方法だと解釈して、質問に更新したとおりに取得するよう変更したのですが、現象自体は改善していません。 */
notgoodpg

2017/12/28 07:02

ご回答ありがとうございます。 1. 別スレッドのコントロールを無理に参照しようとしない。 2. メンバ変数を介して参照する 3. メッセージの送信でコントロールの値は変更する を最終的に満足するよう実装を続けます。 お忙しいなか丁寧な解説を補足していただき大変ありがとうございます。取り急ぎお礼だけ。
guest

0

「別スレッド内ではループで処理を行い、その中ではメインスレッドのコントロールの値を参照しています。」

「別スレッドではスライドコントロールの値を取得する行から処理が進まなくなっているようです。 」
なので、何か異常が発生しているのではないでしょうか。
エラーか何かの場合はループから抜ける処理などは実装されてますか?

また、GetExitCodeThread()の戻り値をチェックしてないのも気になります。
exitCodeとは別に、

C++

1BOOL b = GetExitCodeThread( this->m_hAnotherThread, &exitCode);

のbのような感じで。

投稿2017/12/27 14:50

okrt

総合スコア366

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

notgoodpg

2017/12/28 00:26

ご回答ありがとうございます。 参考にさせていただきまして、コードを修正しました。 しかし、CSliderCtrl::GetPos()関数に処理が移ったらエラーも起こらず値も返らず、処理も進まない状態であることが確認できました。 GetExitCodeThread()の返り値も常にTrueです。 CSliderCtrl::GetPos()内で無限ループでも発生しているかのような動作で問題を引き起こしているようです・・・。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問