「MFCの基礎 - マルチスレッド」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
(Wiki がページ「マルチスレッドを使用する(MFC)」を「MFCの基礎 - マルチスレッド」に、リダイレクトを残さずに移動しました)
 
(同じ利用者による、間の6版が非表示)
3行目: 3行目:


== 作成手順 ==
== 作成手順 ==
まず、クラスにワーカースレッド関数(静的)とワーカースレッド本体の関数を追加する。<br>
# ワーカースレッド制御関数の定義
ワーカースレッド本体の関数では、静的でないメンバ変数およびメンバ関数が使用できる。<br>
#: まず、クラスに静的なワーカースレッド制御関数を定義する。
ワーカースレッド関数では、静的でないメンバ変数およびメンバ関数が使用できないと考えている人がいるが、<br>
#: <br>
下記のサンプルコードのようにすればクラスの関数はワーカースレッド関数にて行うことができる。<br><br>
#: ワーカースレッド制御関数は静的であるため、クラスのメンバ変数およびメンバ関数を直接使用することができない。
#: <u>ただし、<code>void*</code>型の引数を渡して特定のキャストを行うことにより、ワーカースレッド制御関数下でクラスのメンバを使用することができる。</u>
#: <br>
# ワーカースレッド本体の関数の定義
#: 次に、同じクラスに静的ではないワーカースレッド本体の関数を定義する。
#: ワーカースレッド本体の関数では、静的でないメンバ変数およびメンバ関数が使用できる。
<br><br>


== サンプルコード ==
== サンプルコード ==
  <source lang="cpp">
  <syntaxhighlight lang="cpp">
  // CFileView.h
  // CFileView.hファイル
  class CFileView
  class CFileView
  {
  {
17行目: 24行目:
   
   
     public:
     public:
       static UINT LoadXMLThreadFunc(void* pParam); // XMLファイル読み込み時のスレッド制御関数
       static UINT LoadXMLThreadFunc(void *pParam); // 静的なワーカースレッド制御関数
       void        LoadXMLThreadFunc(); // XMLファイル読み込みスレッド処理関数
       void        LoadXMLThreadFunc();             // ワーカースレッド本体
  };
  };
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // CFileView.cpp
  // 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);
                                        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 = FALSE;
       m_pLoadXMLThread->m_bAutoDelete = FALSE;
37行目: 45行目:
       // スレッド処理の開始
       // スレッド処理の開始
       m_pLoadXMLThread->ResumeThread();
       m_pLoadXMLThread->ResumeThread();
       while(WaitForSingleObject(m_pLoadXMLThread->m_hThread, 100) == WAIT_TIMEOUT)
       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 m_pLoadXMLThread;
       delete m_pLoadXMLThread;
     }
     }
53行目: 60行目:
     //m_pLoadXMLThread = ::AfxBeginThread(LoadXMLThreadFunc, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
     //m_pLoadXMLThread = ::AfxBeginThread(LoadXMLThreadFunc, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
   
   
     //if(m_pLoadXMLThread)
     //if(m_pLoadXMLThread) {
    //{
     //  CRect rc;
     //  CRect rc;
     //  HWND hNotifyWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_PROCESSING), this->m_hWnd, (DLGPROC)YCommonProc);
     //  HWND hNotifyWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_PROCESSING), this->m_hWnd, (DLGPROC)YCommonProc);
61行目: 67行目:
     //  ::MoveWindow(hNotifyWnd, (rc.right - rc.left) / 2 - 100, (rc.bottom - rc.top) / 2 - 25, 200, 50, TRUE);
     //  ::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_pMainWnd = this;
     //  m_pLoadXMLThread->m_bAutoDelete = FALSE;
     //  m_pLoadXMLThread->m_bAutoDelete = FALSE;
   
   
     //  m_pLoadXMLThread->ResumeThread();
     //  m_pLoadXMLThread->ResumeThread();
     //  while(::WaitForSingleObject(m_pLoadXMLThread->m_hThread, 100) == WAIT_TIMEOUT)
     //  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);
81行目: 85行目:
  }
  }
   
   
  //----------------------------------------------------------------------------------
  // 静的なワーカースレッド制御関数
  // ワーカースレッド関数(静的)
  // 引数 : この制御関数が定義されているクラスへのポインタ
// @brief   XMLファイル読み込み時のスレッド関数
  // 実処理はワーカースレッド本体の関数で行う
// @param[in]   pParam : 通常CFileViewクラスへのポインタが渡される
// @description  [説明]
//              XMLファイル読み込み処理を別スレッドで行う。
//              ここでは処理本体を呼ぶだけである。
// @return   成功時 0 を返します。
  //----------------------------------------------------------------------------------
  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;
  }
  }
104行目: 102行目:
  void CFileView::LoadXMLThreadFunc()
  void CFileView::LoadXMLThreadFunc()
  {
  {
     // スレッド本体の処理
     // ワーカースレッドで実行する処理
    // ...略
  }
  }
  </source>
  </syntaxhighlight>
<br><br>
<br><br>


== 注意事項 ==
== 画面の更新 ==
UpdateData(TRUE)およびUpdateData(FALSE)等はマルチスレッド内で呼ぶことができない。<br>
ワーカースレッド内において、<code>UpdateData</code>関数等の画面を描画するような関数は実行することができない。<br>
<br>
<code>AfxGetMainWnd</code>関数の戻り値は、m_pLoadXMLThread->m_pMainWndに渡したウィンドウである。<br>
設定しない場合は<code>NULL</code>となり、また、ダイアログの<code>Create</code>関数も呼ぶことができない。<br>
<br>
<br>
AfxGetMainWnd()の戻り値は、m_pLoadXMLThread->m_pMainWndに渡したウィンドウである。<br>
そのため、ワーカースレッドの処理と同期して画面を更新する場合 (UpdateData(TRUE)をスレッドから同期して実行する場合) は、<br>
設定しないとNULLが返り、また、ダイアログのCreate関数も呼ぶことができない。<br>
以下に示すようにワーカースレッド内からメインウィンドウにメッセージを送信する。<br>
そこで、スレッドの処理と同期してUIを更新する場合(UpdateData(TRUE)をスレッドから同期して呼ぶ場合)は、<br>
メインウインドウは、メッセージを受信して任意の処理を行う。<br>
下記のようにスレッド内からメインウィンドウにメッセージを送る。<br>
<br>
<br>
  <source lang="cpp">
  <syntaxhighlight lang="cpp">
  // stdafx.h
  // stdafx.hファイル
   
   
  //------------------------------------------------------------------------
  //------------------------------------------------------------------------
124行目: 125行目:
  //------------------------------------------------------------------------
  //------------------------------------------------------------------------
  #define WM_USER_COMPLETE_LOAD_XML  (WM_USER + 1)  // XMLデータの読み込みの完了メッセージ
  #define WM_USER_COMPLETE_LOAD_XML  (WM_USER + 1)  // XMLデータの読み込みの完了メッセージ
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // CFileView.cpp
  // CFileView.cppファイル
   
   
  // スレッドからメイン処理にメッセージを送る & 受け取って処理
  // まず、ワーカースレッドからメイン処理にメッセージを送信する
// 次に、メッセージを受信して任意の処理を行う
  BEGIN_MESSAGE_MAP(CFileView, CDockablePane)
  BEGIN_MESSAGE_MAP(CFileView, CDockablePane)
     // UserMessage
     // UserMessage
138行目: 140行目:
  // 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());
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // CFileView.h
  // CFileView.hファイル
   
   
  class CFileView
  class CFileView
148行目: 150行目:
     afx_msg LRESULT OnCompleteLoadXML(WPARAM wParam, LPARAM lParam );
     afx_msg LRESULT OnCompleteLoadXML(WPARAM wParam, LPARAM lParam );
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
後は、SendMessage関数を受け取った側で処理を行えばよい。<br>
後は、SendMessage関数を受け取った側で処理を行えばよい。<br>
受け取り側では、UpdateData(TRUE)およびUpdateData(FALSE)等を呼ぶことができる。<br>
受信側では、UpdateData(TRUE)、および、UpdateData(FALSE)等を呼ぶことができる。<br>
<br>
<br>
ここでスレッドを待つWaitForSingleObject関数を使用した場合、デッドロックになるので気をつけること。<br>
<u>この時、スレッドの終了を待機するWaitForSingleObject関数を使用する場合、デッドロックになることに注意する。</u><br>
スレッドの終了時に何らかの処理を行うのであれば、今回のようにスレッド終了時に"メッセージをメインウィンドウへ送る"ことで解決できる。<br>
スレッドの終了時に何らかの処理を行うのであれば、上記のようにスレッド終了時に"メッセージをメインウィンドウへ送信する"ことにより解決できる。<br>
<br><br>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:MFC]]
[[カテゴリ:MFC]]

2024年12月26日 (木) 02:36時点における最新版

概要

MFCアプリケーションにおいて、マルチスレッドを使用する方法について記述する。

作成手順

  1. ワーカースレッド制御関数の定義
    まず、クラスに静的なワーカースレッド制御関数を定義する。

    ワーカースレッド制御関数は静的であるため、クラスのメンバ変数およびメンバ関数を直接使用することができない。
    ただし、void*型の引数を渡して特定のキャストを行うことにより、ワーカースレッド制御関数下でクラスのメンバを使用することができる。

  2. ワーカースレッド本体の関数の定義
    次に、同じクラスに静的ではないワーカースレッド本体の関数を定義する。
    ワーカースレッド本体の関数では、静的でないメンバ変数およびメンバ関数が使用できる。



サンプルコード

 // 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関数を使用する場合、デッドロックになることに注意する。
スレッドの終了時に何らかの処理を行うのであれば、上記のようにスレッド終了時に"メッセージをメインウィンドウへ送信する"ことにより解決できる。