MFCでの簡単なマルチスレッド処理(修正版)
これは、マルチスレッド : プログラミングのヒントから引用すると、「通常、スレッドは自分自身が作成した MFC オブジェクトしかアクセスできません。これは、Windows ハンドルの一時マップとパーマネント マップがスレッド ローカル ストレージに保持されており、複数のスレッドから同時にアクセスできないためです。」とあります。つまり、ワーカスレッド内で、直接画面のコントロールを操作するというプログラミングは問題があるということです。
それではどうするかというと、今回は、やはり、マルチスレッド : プログラミングのヒントに出ている以下の2番目の方法を使ってみました。
「もう 1 つの方法は、ワーカー スレッドが処理するタスクごとに新しいユーザー定義のメッセージを作成し、このメッセージを ::PostMessage 関数を使ってアプリケーションのメイン ウィンドウに渡す方法です。」
具体的には以下のようになるかと思います。プログラム全体の処理としては前回と同じように、ワーカスレッドで時間のかかる処理を仮定して、処理経過をメインダイアログで表示しています。ワーカスレッドではSleep()で待つだけで処理的には何もしていません。
ユーザ定義のメッセージを追加します。(WM_APPはアプリケーション定義のメッセージを定義する場合に使用します。)
#define WM_MYMSG (WM_APP + 1)ダイアログのクラス定義で、以下のようなメッセージハンドラを定義します。
class CMFCThread1Dlg : public CDialog
{
//省略
afx_msg LRESULT OnMyMsg(WPARAM wParam=0, LPARAM lParam=0);
}
メッセージマップに登録します。
BEGIN_MESSAGE_MAP(CMFCThread1Dlg, CDialog)
//{{AFX_MSG_MAP(CMFCThread1Dlg)
//省略
ON_MESSAGE(WM_MYMSG, CMFCThread1Dlg::OnMyMsg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
ワーカスレッドに、ダイアログのウィンドウハンドルを渡します。
//スレッド開始
void CMFCThread1Dlg::OnStart()
{
//省略
//ThreadEntry()をスレッドとして生成する
AfxBeginThread(ThreadEntry,this->m_hWnd);//ウィンドウハンドラを渡す
}
あとは、ワーカスレッド内で、::PostMessage(Win32API)でメッセージを渡して、それをダイアログ内で受け取って、必要な画面更新の処理を行っています。あと、中断されたかどうかのフラグと終了のイベントは単純にグローバル変数で定義しています。修正版ワーカスレッドの処理です。今回は、単純に関数として定義しています。ダイアログのクラスとは全く切り離したC言語レベルの関数です。
//ワーカスレッドとして動作する関数
static UINT ThreadEntry(LPVOID pParam)
{
int i;
//生成元のダイアログのウィンドウハンドラを受け取る
HWND hWnd = (HWND)pParam;
//例えば、3段階の処理があるとします
for(i=1;i<=3;i++){
//処理中メッセージを表示してもらう
PostMessage(hWnd,WM_MYMSG,MYPRM_PROCESSING,i);
//5秒かかる処理と仮定します
Sleep(5*1000);
//中断かどうか判断します
if(bCancel){
//イベントセット
SetEvent(hEvent);
//中断されていたら中断メッセージを表示してもらって、スレッドを抜ける
PostMessage(hWnd,WM_MYMSG,MYPRM_CANCEL,0);
return OK;
}
}
//中断されていなかったら
//イベントセット
if(!bCancel){
SetEvent(hEvent);
//終了メッセージを表示してもらう
PostMessage(hWnd,WM_MYMSG,MYPRM_END,0);
}
return OK;
}
メインダイアログのメッセージハンドラ
//メッセージハンドラ
LRESULT CMFCThread1Dlg::OnMyMsg(WPARAM wParam, LPARAM lParam){
CString csMsg;
CEdit* pEdt = (CEdit*)GetDlgItem(IDC_PROCESS);
switch(wParam){
//処理中の表示
case MYPRM_PROCESSING:
csMsg.Format("処理ステップ %d 実行中",lParam);
pEdt->SetWindowText(csMsg);
break;
//終了の表示、リセット
case MYPRM_END:
pEdt->SetWindowText("終了しました");
CloseHandle(hEvent);
hEvent = NULL;
reset();
break;
//中断の表示、リセット
case MYPRM_CANCEL:
pEdt->SetWindowText("中断しました");
reset();
break;
}
return 0;
}
修正版のソースのプロジェクト一式は以下です。(※これもテストプログラムのレベルですので、動作は保証できません。ご理解、ご了承ください。)プロジェクト一式(Visual Studio6.0形式)
関連リンク
マルチスレッド : プログラミングのヒント
Windowsプログラミング関連記事
Posted by admin at 20:52:14


