「MFCの基礎 - マルチスレッド」の版間の差分
ナビゲーションに移動
検索に移動
編集の要約なし |
(→注意事項) |
||
(3人の利用者による、間の13版が非表示) | |||
3行目: | 3行目: | ||
== 作成手順 == | == 作成手順 == | ||
# ワーカースレッド制御関数の定義 | |||
#: まず、クラスに静的なワーカースレッド制御関数を定義する。 | |||
#: <br> | |||
#: ワーカースレッド制御関数は静的であるため、クラスのメンバ変数およびメンバ関数を直接使用することができない。 | |||
#: <u>ただし、<code>void*</code>型の引数を渡して特定のキャストを行うことにより、ワーカースレッド制御関数下でクラスのメンバを使用することができる。</u> | |||
#: <br> | |||
# ワーカースレッド本体の関数の定義 | |||
#: 次に、同じクラスに静的ではないワーカースレッド本体の関数を定義する。 | |||
#: ワーカースレッド本体の関数では、静的でないメンバ変数およびメンバ関数が使用できる。 | |||
<br><br> | |||
== サンプルコード == | == サンプルコード == | ||
< | <syntaxhighlight lang="cpp"> | ||
// CFileView. | // CFileView.hファイル | ||
class CFileView | class CFileView | ||
{ | { | ||
17行目: | 24行目: | ||
public: | public: | ||
static UINT LoadXMLThreadFunc(void* pParam); // | static UINT LoadXMLThreadFunc(void *pParam); // 静的なワーカースレッド制御関数 | ||
void LoadXMLThreadFunc(); | void LoadXMLThreadFunc(); // ワーカースレッド本体 | ||
}; | }; | ||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// CFileView.cppファイル | |||
void CFileView::OnButtonClick() | void CFileView::OnButtonClick() | ||
{ | { | ||
// XML読み込み処理スレッド関数を実行 | // XML読み込み処理スレッド関数を実行 | ||
m_pLoadXMLThread = ::AfxBeginThread(LoadXMLThreadFunc, this, THREAD_PRIORITY_NORMAL, | m_pLoadXMLThread = ::AfxBeginThread(LoadXMLThreadFunc, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL); | ||
ASSERT(m_pLoadXMLThread); | ASSERT(m_pLoadXMLThread); | ||
if(m_pLoadXMLThread) | |||
if(m_pLoadXMLThread) { | |||
m_pLoadXMLThread->m_pMainWnd = this; | m_pLoadXMLThread->m_pMainWnd = this; | ||
m_pLoadXMLThread->m_bAutoDelete = | m_pLoadXMLThread->m_bAutoDelete = FALSE; | ||
// スレッド処理の開始 | // スレッド処理の開始 | ||
m_pLoadXMLThread->ResumeThread(); | m_pLoadXMLThread->ResumeThread(); | ||
while(WaitForSingleObject( | while (WaitForSingleObject(m_pLoadXMLThread->m_hThread, 100) == WAIT_TIMEOUT) { | ||
// ワーカースレッドが終了するまで待機 | |||
// | |||
MSG msg; | MSG msg; | ||
while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) | while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { | ||
::TranslateMessage(&msg); | ::TranslateMessage(&msg); | ||
::DispatchMessage(&msg); | ::DispatchMessage(&msg); | ||
} | } | ||
} | } | ||
delete | |||
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); | |||
//} | |||
} | } | ||
// | // 静的なワーカースレッド制御関数 | ||
// | // 引数 : この制御関数が定義されているクラスへのポインタ | ||
// 実処理はワーカースレッド本体の関数で行う | |||
// | |||
UINT CFileView::LoadXMLThreadFunc(void *pParam) | UINT CFileView::LoadXMLThreadFunc(void *pParam) | ||
{ | { | ||
CFileView *pFileView = dynamic_cast<CFileView*>(reinterpret_cast<CWnd*>(pParam)); | CFileView *pFileView = dynamic_cast<CFileView*>(reinterpret_cast<CWnd*>(pParam)); | ||
if(pFileView) | if(pFileView) { | ||
pFileView->LoadXMLThreadFunc(); | pFileView->LoadXMLThreadFunc(); | ||
} | } | ||
return THREAD_EXIT_CODE; | return THREAD_EXIT_CODE; | ||
} | } | ||
72行目: | 102行目: | ||
void CFileView::LoadXMLThreadFunc() | void CFileView::LoadXMLThreadFunc() | ||
{ | { | ||
// | // ワーカースレッドで実行する処理 | ||
// ...略 | |||
} | } | ||
</ | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== | == 画面の更新 == | ||
ワーカースレッド内において、<code>UpdateData</code>関数等の画面を描画するような関数は実行することができない。<br> | |||
<br> | <br> | ||
< | <code>AfxGetMainWnd</code>関数の戻り値は、m_pLoadXMLThread->m_pMainWndに渡したウィンドウである。<br> | ||
// | 設定しない場合は<code>NULL</code>となり、また、ダイアログの<code>Create</code>関数も呼ぶことができない。<br> | ||
<br> | |||
そのため、ワーカースレッドの処理と同期して画面を更新する場合 (UpdateData(TRUE)をスレッドから同期して実行する場合) は、<br> | |||
以下に示すようにワーカースレッド内からメインウィンドウにメッセージを送信する。<br> | |||
メインウインドウは、メッセージを受信して任意の処理を行う。<br> | |||
<br> | |||
<syntaxhighlight lang="cpp"> | |||
// stdafx.hファイル | |||
//------------------------------------------------------------------------ | //------------------------------------------------------------------------ | ||
// USER_MESSAGE | // USER_MESSAGE | ||
//------------------------------------------------------------------------ | //------------------------------------------------------------------------ | ||
#define WM_USER_COMPLETE_LOAD_XML (WM_USER + 1) // XMLデータの読み込みの完了メッセージ | #define WM_USER_COMPLETE_LOAD_XML (WM_USER + 1) // XMLデータの読み込みの完了メッセージ | ||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// CFileView.cppファイル | |||
// | // まず、ワーカースレッドからメイン処理にメッセージを送信する | ||
// 次に、メッセージを受信して任意の処理を行う | |||
BEGIN_MESSAGE_MAP(CFileView, CDockablePane) | BEGIN_MESSAGE_MAP(CFileView, CDockablePane) | ||
// UserMessage | // UserMessage | ||
98行目: | 137行目: | ||
END_MESSAGE_MAP() | END_MESSAGE_MAP() | ||
// ワーカースレッド関数の本体にて | // ワーカースレッド関数の本体にて | ||
// XMLファイルの読み込みが完了したメッセージを送信 | // XMLファイルの読み込みが完了したメッセージを送信 | ||
AfxGetMainWnd()->SendMessage(WM_USER_COMPLETE_LOAD_XML, (WPARAM)&firstIndex, (LPARAM)firstFilePath.GetString()); | AfxGetMainWnd()->SendMessage(WM_USER_COMPLETE_LOAD_XML, (WPARAM)&firstIndex, (LPARAM)firstFilePath.GetString()); | ||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// CFileView.hファイル | |||
class CFileView | class CFileView | ||
{ | { | ||
109行目: | 150行目: | ||
afx_msg LRESULT OnCompleteLoadXML(WPARAM wParam, LPARAM lParam ); | afx_msg LRESULT OnCompleteLoadXML(WPARAM wParam, LPARAM lParam ); | ||
} | } | ||
</ | </syntaxhighlight> | ||
<br> | <br> | ||
後は、SendMessage関数を受け取った側で処理を行えばよい。<br> | 後は、SendMessage関数を受け取った側で処理を行えばよい。<br> | ||
受信側では、UpdateData(TRUE)、および、UpdateData(FALSE)等を呼ぶことができる。<br> | |||
<br> | |||
<u>この時、スレッドの終了を待機するWaitForSingleObject関数を使用する場合、デッドロックになることに注意する。</u><br> | |||
スレッドの終了時に何らかの処理を行うのであれば、上記のようにスレッド終了時に"メッセージをメインウィンドウへ送信する"ことにより解決できる。<br> | |||
<br><br> | <br><br> | ||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:MFC]] | [[カテゴリ:MFC]] |
2024年12月26日 (木) 02:36時点における最新版
概要
MFCアプリケーションにおいて、マルチスレッドを使用する方法について記述する。
作成手順
- ワーカースレッド制御関数の定義
- まず、クラスに静的なワーカースレッド制御関数を定義する。
- ワーカースレッド制御関数は静的であるため、クラスのメンバ変数およびメンバ関数を直接使用することができない。
- ただし、
void*
型の引数を渡して特定のキャストを行うことにより、ワーカースレッド制御関数下でクラスのメンバを使用することができる。
- ワーカースレッド本体の関数の定義
- 次に、同じクラスに静的ではないワーカースレッド本体の関数を定義する。
- ワーカースレッド本体の関数では、静的でないメンバ変数およびメンバ関数が使用できる。
サンプルコード
// CFileView.hファイル
class CFileView
{
private:
CWinThread *m_pLoadXMLThread
public:
static UINT LoadXMLThreadFunc(void *pParam); // 静的なワーカースレッド制御関数
void LoadXMLThreadFunc(); // ワーカースレッド本体
};
// 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);
//}
}
// 静的なワーカースレッド制御関数
// 引数 : この制御関数が定義されているクラスへのポインタ
// 実処理はワーカースレッド本体の関数で行う
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
関数等の画面を描画するような関数は実行することができない。
AfxGetMainWnd
関数の戻り値は、m_pLoadXMLThread->m_pMainWndに渡したウィンドウである。
設定しない場合はNULL
となり、また、ダイアログのCreate
関数も呼ぶことができない。
そのため、ワーカースレッドの処理と同期して画面を更新する場合 (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関数を使用する場合、デッドロックになることに注意する。
スレッドの終了時に何らかの処理を行うのであれば、上記のようにスレッド終了時に"メッセージをメインウィンドウへ送信する"ことにより解決できる。