「MFCの基礎 - マルチスレッド」の版間の差分
ナビゲーションに移動
検索に移動
(→注意事項) |
細 (Wiki がページ「マルチスレッドを使用する(MFC)」を「MFCの基礎 - マルチスレッド」に、リダイレクトを残さずに移動しました) |
2021年1月29日 (金) 15:34時点における版
概要
MFCアプリケーションにおいて、マルチスレッドを使用する方法について記述する。
作成手順
まず、クラスにワーカースレッド関数(静的)とワーカースレッド本体の関数を追加する。
ワーカースレッド本体の関数では、静的でないメンバ変数およびメンバ関数が使用できる。
ワーカースレッド関数では、静的でないメンバ変数およびメンバ関数が使用できないと考えている人がいるが、
下記のサンプルコードのようにすればクラスの関数はワーカースレッド関数にて行うことができる。
サンプルコード
// CFileView.h
class CFileView
{
private:
CWinThread *m_pLoadXMLThread
public:
static UINT LoadXMLThreadFunc(void* pParam); // XMLファイル読み込み時のスレッド制御関数
void LoadXMLThreadFunc(); // XMLファイル読み込みスレッド処理関数
};
// CFileView.cpp
void CFileView::OnButtonClick()
{
// XML読み込み処理スレッド関数を実行
m_pLoadXMLThread = ::AfxBeginThread(LoadXMLThreadFunc, this, THREAD_PRIORITY_NORMAL,
0, CREATE_SUSPENDED, NULL);
ASSERT(m_pLoadXMLThread);
if(m_pLoadXMLThread)
{
m_pLoadXMLThread->m_pMainWnd = this;
m_pLoadXMLThread->m_bAutoDelete = FALSE;
// スレッド処理の開始
m_pLoadXMLThread->ResumeThread();
while(WaitForSingleObject(m_pLoadXMLThread->m_hThread, 100) == WAIT_TIMEOUT)
{
// フリーズしないようにする
MSG msg;
while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
delete m_pLoadXMLThread;
}
// マルチスレッドの応用(ウェイトウィンドウを表示する)
//m_pLoadXMLThread = ::AfxBeginThread(LoadXMLThreadFunc, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
//if(m_pLoadXMLThread)
//{
// CRect rc;
// HWND hNotifyWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_PROCESSING), this->m_hWnd, (DLGPROC)YCommonProc);
// ::ShowWindow(hNotifyWnd, SW_SHOW);
// ::GetClientRect(&rc);
// ::MoveWindow(hNotifyWnd, (rc.right - rc.left) / 2 - 100, (rc.bottom - rc.top) / 2 - 25, 200, 50, TRUE);
// m_pLoadXMLThread->m_pMainWnd = this;
// m_pLoadXMLThread->m_bAutoDelete = FALSE;
// m_pLoadXMLThread->ResumeThread();
// while(::WaitForSingleObject(m_pLoadXMLThread->m_hThread, 100) == WAIT_TIMEOUT)
// {
// // フリーズしないようにする
// MSG msg;
// while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
// {
// ::TranslateMessage(&msg);
// ::DispatchMessage(&msg);
// }
// }
// delete m_pLoadXMLThread;
// ::DestroyWindow(hNotifyWnd);
//}
}
//----------------------------------------------------------------------------------
// ワーカースレッド関数(静的)
// @brief XMLファイル読み込み時のスレッド関数
// @param[in] pParam : 通常CFileViewクラスへのポインタが渡される
// @description [説明]
// XMLファイル読み込み処理を別スレッドで行う。
// ここでは処理本体を呼ぶだけである。
// @return 成功時 0 を返します。
//----------------------------------------------------------------------------------
UINT CFileView::LoadXMLThreadFunc(void *pParam)
{
CFileView *pFileView = dynamic_cast<CFileView*>(reinterpret_cast<CWnd*>(pParam));
if(pFileView)
{
pFileView->LoadXMLThreadFunc();
}
return THREAD_EXIT_CODE;
}
// ワーカースレッド本体の関数
// スレッドの開始
void CFileView::LoadXMLThreadFunc()
{
// スレッド本体の処理
}
注意事項
UpdateData(TRUE)およびUpdateData(FALSE)等はマルチスレッド内で呼ぶことができない。
AfxGetMainWnd()の戻り値は、m_pLoadXMLThread->m_pMainWndに渡したウィンドウである。
設定しないとNULLが返り、また、ダイアログのCreate関数も呼ぶことができない。
そこで、スレッドの処理と同期してUIを更新する場合(UpdateData(TRUE)をスレッドから同期して呼ぶ場合)は、
下記のようにスレッド内からメインウィンドウにメッセージを送る。
// stdafx.h
//------------------------------------------------------------------------
// USER_MESSAGE
//------------------------------------------------------------------------
#define WM_USER_COMPLETE_LOAD_XML (WM_USER + 1) // XMLデータの読み込みの完了メッセージ
// CFileView.cpp
// スレッドからメイン処理にメッセージを送る & 受け取って処理
BEGIN_MESSAGE_MAP(CFileView, CDockablePane)
// UserMessage
ON_MESSAGE(WM_USER_COMPLETE_LOAD_XML, &CFileView::OnCompleteLoadXML)
END_MESSAGE_MAP()
// ワーカースレッド関数の本体にて
// XMLファイルの読み込みが完了したメッセージを送信
AfxGetMainWnd()->SendMessage(WM_USER_COMPLETE_LOAD_XML, (WPARAM)&firstIndex, (LPARAM)firstFilePath.GetString());
// CFileView.h
class CFileView
{
// 追加
afx_msg LRESULT OnCompleteLoadXML(WPARAM wParam, LPARAM lParam );
}
後は、SendMessage関数を受け取った側で処理を行えばよい。
受け取り側では、UpdateData(TRUE)およびUpdateData(FALSE)等を呼ぶことができる。
ここでスレッドを待つWaitForSingleObject関数を使用した場合、デッドロックになるので気をつけること。
スレッドの終了時に何らかの処理を行うのであれば、今回のようにスレッド終了時に"メッセージをメインウィンドウへ送る"ことで解決できる。