MFCの基礎 - マルチスレッド

提供:MochiuWiki : SUSE, EC, PCB
2019年7月18日 (木) 02:13時点における192.168.1.23による版
ナビゲーションに移動 検索に移動

概要

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	= TRUE;
 
       // スレッド処理の開始
       m_pLoadXMLThread->ResumeThread();
       while(WaitForSingleObject(pTh->m_hThread, 100) == WAIT_TIMEOUT)
       {
          // フリーズしないようにする
          MSG msg;
          while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
          {
             ::TranslateMessage(&msg);
             ::DispatchMessage(&msg);
          }
       }
       delete pTh;
    }
 }
 
 //----------------------------------------------------------------------------------
 // ワーカースレッド関数(静的)
 // @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()の戻り値は、CWinThread *m_pLoadXMLThread->m_pMainWndに渡したウィンドウである。
設定しないとNULLが返り、また、ダイアログのCreate関数も呼ぶことができない。
そこで、スレッドの処理と同期してUIを更新する場合(UpdateData(TRUE)をスレッドから同期して呼ぶ場合)は、
下記のようにスレッド内からメインウィンドウにメッセージを送る。

 // スレッドからメイン処理にメッセージを送る & 受け取って処理
 //------------------------------------------------------------------------
 // USER_MESSAGE
 //------------------------------------------------------------------------
 // stdafx.h
 #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()
 
 // CFileView.cpp
 // ワーカースレッド関数の本体にて
 // 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関数を使用した場合、デッドロックになるので気をつけること。
スレッドの終了時に何らかの処理を行うのであれば、今回のようにスレッド終了時に"メッセージをメインウィンドウへ送る"ことで解決できる。