プロが教えるわが家の防犯対策術!

お世話になります。
VC++でスレッドプログラムを作っています。
AfxBeginThreadでワーカースレッドを作成し、その中でダイアログを表示する
プログラムです。

問題は、ワーカースレッドがある処理状態に至ったことを
メインスレッドで検知したいのですが、それがうまく
いかないということです。

コードの概略をしめします。

<メインスレッド側>
void CTestApp::OnTest()
{
  CTestDlg dlg;
  dlg.IsContinue = FALSE;

  pThread = AfxBeginThread(TestDlg::Thread, (void*)&dlg);
  while (!dlg.IsContinue) { // <----問題:このループ処理を抜けれない!
   Sleep(0);
  }
}

<ワーカースレッド側>
UINT TestDlg::Thread(LPVOID pParam){
  TestDlg* pDlg = (TestDlg*)pParam;
  pDlg->Create(TestDlg::IDD);
  pDlg->ShowWindow(SW_SHOW);
 
  pDlg->IsContinue = TRUE; //<--ここでフラグを変更しているのに。

  while(pDlg->IsContinue) {
   MSG msg;
   if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
     ::TranslateMessage(&msg);
     ::DispatchMessage(&msg);
   }
  }
  pDlg->DestroyWindow();
  return 0;
}

----
メインスレッドのSleep(0)で待つのを
AfxGetApp()->PumpMessage()に変更すると
ループを抜けるようなのですが、どうも納得できません。

上記のプログラムで問題、もしくはプロジェクトの
設定等に不備がある可能性がありましたら
御教授いただけたら幸いです。
よろしくおねがいいたします。

A 回答 (2件)

失礼。



CTestDlg dlg;がメインにあるというよりも、
問題はpDlg->Create(TestDlg::IDD);こっちぽいですね。

CTestDlgがMFCのダイアログの派生だとすると、
Create時に親を指定しない場合(このケースはそう)、
親ウィンドウがメインウィンドウ(メインスレッド側)になりますので、
ウィンドウ破棄の時に親側がただしく処理されてない、と。
# MFCのウィンドウは内部にTLSなど使ってるので、むやみにスレッドを
# 跨がせると危険なため、一見そちらかと思いましたが別原因ぽい。

> あと、OnTestはコールバックでなく
> メインスレッド側の単なるコマンド実行の関数です。
> 説明不足ですみません。

ここでは、メインスレッドのメッセージポンプを止めるか否かが焦点ですので、
Test用ボタンが押されたときに動くとか、Menuが押されたときに動くとか、
MFCのウィンドウメッセージで動くものは、
内部処理はWin32のWindow Procedureから呼ばれるコールバックの一種です。

# 基本的にMFC関係のウィンドウ自体は全部メインスレッドにしないと、
# 処理のたびに内部でスレッド内にまたがった同期が発生したりしますので、
# そもそもメインで安易にwhileしないとか、実処理だけをワーカにするなど検討させることをお勧めします。
# ・ワーカ側にDlgを直接渡す必要性は普通ないです。
# ・ワーカでCreateなんてのも普通しません。(せめてUIスレッドならまだしも)
# ・ダイアログをメインで作って、内部処理だけワーカで行ってもお望みのことはできます。
# ・設計上、UIと内部処理は極力分離するのが好ましいです。
    • good
    • 0
この回答へのお礼

MrBanさん
詳しい回答をいただきまして
ありがとうございます。

確かに、pDlg->Create(TestDlg::IDD);
を実行したときに、処理がとまっていました。

ワーカースレッドの中で、ダイアログ生成~破棄するのを
やめて、メインスレッドに移すとうまく動くようになりました。

CWnd* と HWND のパーマネントマップがスレッドごとに
異なるため、スレッドを跨いで親ウィンドウにアクセスすると
ハングしてしまうようですね。

今後は極力、ワーカスレッドにはウィンドウを渡さない
設計でプログラムしようと思います。

アドバイスをいただきまして
まことにありがとうございました。

お礼日時:2007/05/15 14:55

> pDlg->DestroyWindow();



多分これのせいです。

> CTestDlg dlg;
をメイン側においてるので、メイン側のスレッドコンテキストでウィンドウが動いてると思われます。

ワーカ側でウィンドウを破棄しようとすると、
メイン側のメッセージ処理が動く必要がありますが、
メイン側はOnTestという(恐らく)コールバック内でループしているため、
メッセージが処理されずに止まってしまいます。

PumpMessageにするととりあえず内部でメッセージが処理されるので、
破棄されるようになると思いますが、正直あまりお勧めの処理とはいえません。

# MFCのウィンドウはスレッド跨ぎで処理するとハマルことがあったり、
# 念のためvolatileをつけておくべき、とか、
# メインスレッドでむやみにループするべきではない、とか、
# 根本的に気になる点もいくつかありますが…。

この回答への補足

MrBanさん。
回答をいただきましてありがとうございます。

>CTestDlg dlg;
>をメイン側においてるので、メイン側のスレッドコンテキスト
>でウィンドウが動いてると思われます。

確かに、dlgオブジェクトはメインスレッドにありますが、
ウィンドウの生成は
pDlg->Create(TestDlg::IDD);
でスレッド内で行っています。
そうするとウィンドウはスレッドコンテキストで
動くと思うのですが、間違っていますか?

メイン側の
TestDlg dlg;
は、単なるオブジェクトをスタックに置いている
だけのような気がするのですが、
これだけで、メインスレッド側でウィンドウが
動くことになるのでしょうか?

あと、OnTestはコールバックでなく
メインスレッド側の単なるコマンド実行の関数です。
説明不足ですみません。

よろしく、お願いいたします。

補足日時:2007/05/13 10:43
    • good
    • 0

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